mcp-code-indexer 2.3.0__py3-none-any.whl → 3.0.0__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.
@@ -31,6 +31,7 @@ from mcp_code_indexer.database.connection_health import (
31
31
  ConnectionHealthMonitor, DatabaseMetricsCollector
32
32
  )
33
33
  from mcp_code_indexer.query_preprocessor import preprocess_search_query
34
+ from mcp_code_indexer.cleanup_manager import CleanupManager
34
35
 
35
36
  logger = logging.getLogger(__name__)
36
37
 
@@ -79,6 +80,9 @@ class DatabaseManager:
79
80
  self._health_monitor = None # Initialized in async context
80
81
  self._metrics_collector = DatabaseMetricsCollector()
81
82
 
83
+ # Cleanup manager for retention policies
84
+ self._cleanup_manager = None # Initialized in async context
85
+
82
86
  async def initialize(self) -> None:
83
87
  """Initialize database schema and configuration."""
84
88
  import asyncio
@@ -97,6 +101,9 @@ class DatabaseManager:
97
101
  )
98
102
  await self._health_monitor.start_monitoring()
99
103
 
104
+ # Initialize cleanup manager
105
+ self._cleanup_manager = CleanupManager(self, retention_months=6)
106
+
100
107
  # Ensure database directory exists
101
108
  self.db_path.parent.mkdir(parents=True, exist_ok=True)
102
109
 
@@ -703,20 +710,7 @@ class DatabaseManager:
703
710
 
704
711
  return projects
705
712
 
706
- async def get_branch_file_counts(self, project_id: str) -> Dict[str, int]:
707
- """Get file counts per branch for a project."""
708
- async with self.get_connection() as db:
709
- cursor = await db.execute(
710
- """
711
- SELECT branch, COUNT(*) as file_count
712
- FROM file_descriptions
713
- WHERE project_id = ?
714
- GROUP BY branch
715
- """,
716
- (project_id,)
717
- )
718
- rows = await cursor.fetchall()
719
- return {row[0]: row[1] for row in rows}
713
+
720
714
 
721
715
  # File description operations
722
716
 
@@ -726,18 +720,18 @@ class DatabaseManager:
726
720
  await db.execute(
727
721
  """
728
722
  INSERT OR REPLACE INTO file_descriptions
729
- (project_id, branch, file_path, description, file_hash, last_modified, version, source_project_id)
723
+ (project_id, file_path, description, file_hash, last_modified, version, source_project_id, to_be_cleaned)
730
724
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
731
725
  """,
732
726
  (
733
727
  file_desc.project_id,
734
- file_desc.branch,
735
728
  file_desc.file_path,
736
729
  file_desc.description,
737
730
  file_desc.file_hash,
738
731
  file_desc.last_modified,
739
732
  file_desc.version,
740
- file_desc.source_project_id
733
+ file_desc.source_project_id,
734
+ file_desc.to_be_cleaned
741
735
  )
742
736
  )
743
737
  await db.commit()
@@ -746,60 +740,60 @@ class DatabaseManager:
746
740
  async def get_file_description(
747
741
  self,
748
742
  project_id: str,
749
- branch: str,
750
743
  file_path: str
751
744
  ) -> Optional[FileDescription]:
752
- """Get file description by project, branch, and path."""
745
+ """Get file description by project and path."""
753
746
  async with self.get_connection() as db:
754
747
  cursor = await db.execute(
755
748
  """
756
749
  SELECT * FROM file_descriptions
757
- WHERE project_id = ? AND branch = ? AND file_path = ?
750
+ WHERE project_id = ? AND file_path = ? AND to_be_cleaned IS NULL
758
751
  """,
759
- (project_id, branch, file_path)
752
+ (project_id, file_path)
760
753
  )
761
754
  row = await cursor.fetchone()
762
755
 
763
756
  if row:
764
757
  return FileDescription(
758
+ id=row['id'],
765
759
  project_id=row['project_id'],
766
- branch=row['branch'],
767
760
  file_path=row['file_path'],
768
761
  description=row['description'],
769
762
  file_hash=row['file_hash'],
770
763
  last_modified=datetime.fromisoformat(row['last_modified']),
771
764
  version=row['version'],
772
- source_project_id=row['source_project_id']
765
+ source_project_id=row['source_project_id'],
766
+ to_be_cleaned=row['to_be_cleaned']
773
767
  )
774
768
  return None
775
769
 
776
770
  async def get_all_file_descriptions(
777
771
  self,
778
- project_id: str,
779
- branch: str
772
+ project_id: str
780
773
  ) -> List[FileDescription]:
781
- """Get all file descriptions for a project and branch."""
774
+ """Get all file descriptions for a project."""
782
775
  async with self.get_connection() as db:
783
776
  cursor = await db.execute(
784
777
  """
785
778
  SELECT * FROM file_descriptions
786
- WHERE project_id = ? AND branch = ?
779
+ WHERE project_id = ? AND to_be_cleaned IS NULL
787
780
  ORDER BY file_path
788
781
  """,
789
- (project_id, branch)
782
+ (project_id,)
790
783
  )
791
784
  rows = await cursor.fetchall()
792
785
 
793
786
  return [
794
787
  FileDescription(
788
+ id=row['id'],
795
789
  project_id=row['project_id'],
796
- branch=row['branch'],
797
790
  file_path=row['file_path'],
798
791
  description=row['description'],
799
792
  file_hash=row['file_hash'],
800
793
  last_modified=datetime.fromisoformat(row['last_modified']),
801
794
  version=row['version'],
802
- source_project_id=row['source_project_id']
795
+ source_project_id=row['source_project_id'],
796
+ to_be_cleaned=row['to_be_cleaned']
803
797
  )
804
798
  for row in rows
805
799
  ]
@@ -813,13 +807,13 @@ class DatabaseManager:
813
807
  data = [
814
808
  (
815
809
  fd.project_id,
816
- fd.branch,
817
810
  fd.file_path,
818
811
  fd.description,
819
812
  fd.file_hash,
820
813
  fd.last_modified,
821
814
  fd.version,
822
- fd.source_project_id
815
+ fd.source_project_id,
816
+ fd.to_be_cleaned
823
817
  )
824
818
  for fd in file_descriptions
825
819
  ]
@@ -827,7 +821,7 @@ class DatabaseManager:
827
821
  await conn.executemany(
828
822
  """
829
823
  INSERT OR REPLACE INTO file_descriptions
830
- (project_id, branch, file_path, description, file_hash, last_modified, version, source_project_id)
824
+ (project_id, file_path, description, file_hash, last_modified, version, source_project_id, to_be_cleaned)
831
825
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
832
826
  """,
833
827
  data
@@ -845,7 +839,6 @@ class DatabaseManager:
845
839
  async def search_file_descriptions(
846
840
  self,
847
841
  project_id: str,
848
- branch: str,
849
842
  query: str,
850
843
  max_results: int = 20
851
844
  ) -> List[SearchResult]:
@@ -864,26 +857,24 @@ class DatabaseManager:
864
857
  """
865
858
  SELECT
866
859
  fd.project_id,
867
- fd.branch,
868
860
  fd.file_path,
869
861
  fd.description,
870
862
  bm25(file_descriptions_fts) as rank
871
863
  FROM file_descriptions_fts
872
- JOIN file_descriptions fd ON fd.rowid = file_descriptions_fts.rowid
864
+ JOIN file_descriptions fd ON fd.id = file_descriptions_fts.rowid
873
865
  WHERE file_descriptions_fts MATCH ?
874
866
  AND fd.project_id = ?
875
- AND fd.branch = ?
867
+ AND fd.to_be_cleaned IS NULL
876
868
  ORDER BY bm25(file_descriptions_fts)
877
869
  LIMIT ?
878
870
  """,
879
- (preprocessed_query, project_id, branch, max_results)
871
+ (preprocessed_query, project_id, max_results)
880
872
  )
881
873
  rows = await cursor.fetchall()
882
874
 
883
875
  return [
884
876
  SearchResult(
885
877
  project_id=row['project_id'],
886
- branch=row['branch'],
887
878
  file_path=row['file_path'],
888
879
  description=row['description'],
889
880
  relevance_score=row['rank']
@@ -936,12 +927,12 @@ class DatabaseManager:
936
927
 
937
928
  # Utility operations
938
929
 
939
- async def get_file_count(self, project_id: str, branch: str) -> int:
940
- """Get count of files in a project branch."""
930
+ async def get_file_count(self, project_id: str) -> int:
931
+ """Get count of files in a project."""
941
932
  async with self.get_connection() as db:
942
933
  cursor = await db.execute(
943
- "SELECT COUNT(*) as count FROM file_descriptions WHERE project_id = ? AND branch = ?",
944
- (project_id, branch)
934
+ "SELECT COUNT(*) as count FROM file_descriptions WHERE project_id = ? AND to_be_cleaned IS NULL",
935
+ (project_id,)
945
936
  )
946
937
  row = await cursor.fetchone()
947
938
  return row['count'] if row else 0
@@ -1030,12 +1021,11 @@ class DatabaseManager:
1030
1021
  await db.execute(
1031
1022
  """
1032
1023
  INSERT OR REPLACE INTO project_overviews
1033
- (project_id, branch, overview, last_modified, total_files, total_tokens)
1034
- VALUES (?, ?, ?, ?, ?, ?)
1024
+ (project_id, overview, last_modified, total_files, total_tokens)
1025
+ VALUES (?, ?, ?, ?, ?)
1035
1026
  """,
1036
1027
  (
1037
1028
  overview.project_id,
1038
- overview.branch,
1039
1029
  overview.overview,
1040
1030
  overview.last_modified,
1041
1031
  overview.total_files,
@@ -1043,21 +1033,20 @@ class DatabaseManager:
1043
1033
  )
1044
1034
  )
1045
1035
  await db.commit()
1046
- logger.debug(f"Created/updated overview for project {overview.project_id}, branch {overview.branch}")
1036
+ logger.debug(f"Created/updated overview for project {overview.project_id}")
1047
1037
 
1048
- async def get_project_overview(self, project_id: str, branch: str) -> Optional[ProjectOverview]:
1049
- """Get project overview by ID and branch."""
1038
+ async def get_project_overview(self, project_id: str) -> Optional[ProjectOverview]:
1039
+ """Get project overview by ID."""
1050
1040
  async with self.get_connection() as db:
1051
1041
  cursor = await db.execute(
1052
- "SELECT * FROM project_overviews WHERE project_id = ? AND branch = ?",
1053
- (project_id, branch)
1042
+ "SELECT * FROM project_overviews WHERE project_id = ?",
1043
+ (project_id,)
1054
1044
  )
1055
1045
  row = await cursor.fetchone()
1056
1046
 
1057
1047
  if row:
1058
1048
  return ProjectOverview(
1059
1049
  project_id=row['project_id'],
1060
- branch=row['branch'],
1061
1050
  overview=row['overview'],
1062
1051
  last_modified=datetime.fromisoformat(row['last_modified']),
1063
1052
  total_files=row['total_files'],
@@ -1065,25 +1054,24 @@ class DatabaseManager:
1065
1054
  )
1066
1055
  return None
1067
1056
 
1068
- async def cleanup_missing_files(self, project_id: str, branch: str, project_root: Path) -> List[str]:
1057
+ async def cleanup_missing_files(self, project_id: str, project_root: Path) -> List[str]:
1069
1058
  """
1070
- Remove descriptions for files that no longer exist on disk.
1059
+ Mark descriptions for cleanup for files that no longer exist on disk.
1071
1060
 
1072
1061
  Args:
1073
1062
  project_id: Project identifier
1074
- branch: Branch name
1075
1063
  project_root: Path to project root directory
1076
1064
 
1077
1065
  Returns:
1078
- List of file paths that were cleaned up
1066
+ List of file paths that were marked for cleanup
1079
1067
  """
1080
1068
  removed_files = []
1081
1069
 
1082
1070
  async def cleanup_operation(conn: aiosqlite.Connection) -> List[str]:
1083
- # Get all file descriptions for this project/branch
1071
+ # Get all active file descriptions for this project
1084
1072
  cursor = await conn.execute(
1085
- "SELECT file_path FROM file_descriptions WHERE project_id = ? AND branch = ?",
1086
- (project_id, branch)
1073
+ "SELECT file_path FROM file_descriptions WHERE project_id = ? AND to_be_cleaned IS NULL",
1074
+ (project_id,)
1087
1075
  )
1088
1076
 
1089
1077
  rows = await cursor.fetchall()
@@ -1097,31 +1085,32 @@ class DatabaseManager:
1097
1085
  if not full_path.exists():
1098
1086
  to_remove.append(file_path)
1099
1087
 
1100
- # Remove descriptions for missing files
1088
+ # Mark descriptions for cleanup instead of deleting
1101
1089
  if to_remove:
1090
+ import time
1091
+ cleanup_timestamp = int(time.time())
1102
1092
  await conn.executemany(
1103
- "DELETE FROM file_descriptions WHERE project_id = ? AND branch = ? AND file_path = ?",
1104
- [(project_id, branch, path) for path in to_remove]
1093
+ "UPDATE file_descriptions SET to_be_cleaned = ? WHERE project_id = ? AND file_path = ?",
1094
+ [(cleanup_timestamp, project_id, path) for path in to_remove]
1105
1095
  )
1106
- logger.info(f"Cleaned up {len(to_remove)} missing files from {project_id}/{branch}")
1096
+ logger.info(f"Marked {len(to_remove)} missing files for cleanup from {project_id}")
1107
1097
 
1108
1098
  return to_remove
1109
1099
 
1110
1100
  removed_files = await self.execute_transaction_with_retry(
1111
1101
  cleanup_operation,
1112
- f"cleanup_missing_files_{project_id}_{branch}",
1102
+ f"cleanup_missing_files_{project_id}",
1113
1103
  timeout_seconds=60.0 # Longer timeout for file system operations
1114
1104
  )
1115
1105
 
1116
1106
  return removed_files
1117
1107
 
1118
- async def analyze_word_frequency(self, project_id: str, branch: str, limit: int = 200) -> WordFrequencyResult:
1108
+ async def analyze_word_frequency(self, project_id: str, limit: int = 200) -> WordFrequencyResult:
1119
1109
  """
1120
- Analyze word frequency across all file descriptions for a project/branch.
1110
+ Analyze word frequency across all file descriptions for a project.
1121
1111
 
1122
1112
  Args:
1123
1113
  project_id: Project identifier
1124
- branch: Branch name
1125
1114
  limit: Maximum number of top terms to return
1126
1115
 
1127
1116
  Returns:
@@ -1152,10 +1141,10 @@ class DatabaseManager:
1152
1141
  stop_words.update(programming_keywords)
1153
1142
 
1154
1143
  async with self.get_connection() as db:
1155
- # Get all descriptions for this project/branch
1144
+ # Get all descriptions for this project
1156
1145
  cursor = await db.execute(
1157
- "SELECT description FROM file_descriptions WHERE project_id = ? AND branch = ?",
1158
- (project_id, branch)
1146
+ "SELECT description FROM file_descriptions WHERE project_id = ? AND to_be_cleaned IS NULL",
1147
+ (project_id,)
1159
1148
  )
1160
1149
 
1161
1150
  rows = await cursor.fetchall()
@@ -1218,13 +1207,12 @@ class DatabaseManager:
1218
1207
  await db.commit()
1219
1208
  return removed_count
1220
1209
 
1221
- async def get_project_map_data(self, project_identifier: str, branch: str = None) -> dict:
1210
+ async def get_project_map_data(self, project_identifier: str) -> dict:
1222
1211
  """
1223
1212
  Get all data needed to generate a project map.
1224
1213
 
1225
1214
  Args:
1226
1215
  project_identifier: Project name or ID
1227
- branch: Branch name (optional, will use first available if not specified)
1228
1216
 
1229
1217
  Returns:
1230
1218
  Dictionary containing project info, overview, and file descriptions
@@ -1256,39 +1244,43 @@ class DatabaseManager:
1256
1244
 
1257
1245
  project = Project(**project_dict)
1258
1246
 
1259
- # If no branch specified, find the first available branch
1260
- if not branch:
1261
- cursor = await db.execute(
1262
- "SELECT DISTINCT branch FROM file_descriptions WHERE project_id = ? LIMIT 1",
1263
- (project.id,)
1264
- )
1265
- branch_row = await cursor.fetchone()
1266
- if branch_row:
1267
- branch = branch_row['branch']
1268
- else:
1269
- branch = 'main' # Default fallback
1270
-
1271
1247
  # Get project overview
1272
1248
  cursor = await db.execute(
1273
- "SELECT * FROM project_overviews WHERE project_id = ? AND branch = ?",
1274
- (project.id, branch)
1249
+ "SELECT * FROM project_overviews WHERE project_id = ?",
1250
+ (project.id,)
1275
1251
  )
1276
1252
  overview_row = await cursor.fetchone()
1277
1253
  project_overview = ProjectOverview(**overview_row) if overview_row else None
1278
1254
 
1279
- # Get all file descriptions for this project/branch
1255
+ # Get all file descriptions for this project
1280
1256
  cursor = await db.execute(
1281
1257
  """SELECT * FROM file_descriptions
1282
- WHERE project_id = ? AND branch = ?
1258
+ WHERE project_id = ? AND to_be_cleaned IS NULL
1283
1259
  ORDER BY file_path""",
1284
- (project.id, branch)
1260
+ (project.id,)
1285
1261
  )
1286
1262
  file_rows = await cursor.fetchall()
1287
1263
  file_descriptions = [FileDescription(**row) for row in file_rows]
1288
1264
 
1289
1265
  return {
1290
1266
  'project': project,
1291
- 'branch': branch,
1292
1267
  'overview': project_overview,
1293
1268
  'files': file_descriptions
1294
1269
  }
1270
+
1271
+ # Cleanup operations
1272
+
1273
+ @property
1274
+ def cleanup_manager(self) -> CleanupManager:
1275
+ """Get the cleanup manager instance."""
1276
+ if self._cleanup_manager is None:
1277
+ self._cleanup_manager = CleanupManager(self, retention_months=6)
1278
+ return self._cleanup_manager
1279
+
1280
+ async def mark_file_for_cleanup(self, project_id: str, file_path: str) -> bool:
1281
+ """Mark a file for cleanup. Convenience method."""
1282
+ return await self.cleanup_manager.mark_file_for_cleanup(project_id, file_path)
1283
+
1284
+ async def perform_cleanup(self, project_id: Optional[str] = None) -> int:
1285
+ """Perform cleanup of old records. Convenience method."""
1286
+ return await self.cleanup_manager.perform_cleanup(project_id)
@@ -29,19 +29,20 @@ class Project(BaseModel):
29
29
 
30
30
  class FileDescription(BaseModel):
31
31
  """
32
- Represents a file description within a project branch.
32
+ Represents a file description within a project.
33
33
 
34
34
  Stores detailed summaries of file contents including purpose, components,
35
35
  and relationships to enable efficient codebase navigation.
36
36
  """
37
+ id: Optional[int] = Field(None, description="Database ID")
37
38
  project_id: str = Field(..., description="Reference to project")
38
- branch: str = Field(..., description="Git branch name")
39
39
  file_path: str = Field(..., description="Relative path from project root")
40
40
  description: str = Field(..., description="Detailed content description")
41
41
  file_hash: Optional[str] = Field(None, description="SHA-256 of file contents")
42
42
  last_modified: datetime = Field(default_factory=datetime.utcnow, description="Last update timestamp")
43
43
  version: int = Field(default=1, description="For optimistic concurrency control")
44
44
  source_project_id: Optional[str] = Field(None, description="Source project if copied from upstream")
45
+ to_be_cleaned: Optional[int] = Field(None, description="UNIX timestamp for cleanup, NULL = active")
45
46
 
46
47
 
47
48
  class MergeConflict(BaseModel):
@@ -71,7 +72,6 @@ class ProjectOverview(BaseModel):
71
72
  individual file descriptions.
72
73
  """
73
74
  project_id: str = Field(..., description="Reference to project")
74
- branch: str = Field(..., description="Git branch name")
75
75
  overview: str = Field(..., description="Comprehensive codebase narrative")
76
76
  last_modified: datetime = Field(default_factory=datetime.utcnow, description="Last update timestamp")
77
77
  total_files: int = Field(..., description="Number of files in codebase")
@@ -86,7 +86,6 @@ class CodebaseOverview(BaseModel):
86
86
  to help determine whether to use full overview or search-based approach.
87
87
  """
88
88
  project_name: str = Field(..., description="Project name")
89
- branch: str = Field(..., description="Git branch")
90
89
  total_files: int = Field(..., description="Total number of tracked files")
91
90
  total_tokens: int = Field(..., description="Total token count for all descriptions")
92
91
  is_large: bool = Field(..., description="True if exceeds configured token limit")
@@ -121,7 +120,6 @@ class SearchResult(BaseModel):
121
120
  description: str = Field(..., description="File description")
122
121
  relevance_score: float = Field(..., description="Search relevance score")
123
122
  project_id: str = Field(..., description="Project identifier")
124
- branch: str = Field(..., description="Git branch")
125
123
 
126
124
 
127
125
  class CodebaseSizeInfo(BaseModel):
@@ -75,7 +75,7 @@ class DeepAskHandler(ClaudeAPIHandler):
75
75
  Ask an enhanced question about the project using two-stage Claude API processing.
76
76
 
77
77
  Args:
78
- project_info: Project information dict with projectName, folderPath, branch, etc.
78
+ project_info: Project information dict with projectName, folderPath, etc.
79
79
  question: User's question about the project
80
80
  max_file_results: Maximum number of file descriptions to include
81
81
 
@@ -118,8 +118,7 @@ class DeepAskHandler(ClaudeAPIHandler):
118
118
  "stage1_tokens": stage1_result["token_usage"],
119
119
  "stage2_tokens": stage2_result["token_usage"],
120
120
  "total_files_found": stage2_result["total_files_found"],
121
- "files_included": len(stage2_result["relevant_files"]),
122
- "branch": project_info.get("branch", "unknown")
121
+ "files_included": len(stage2_result["relevant_files"])
123
122
  }
124
123
  }
125
124
 
@@ -237,7 +236,6 @@ class DeepAskHandler(ClaudeAPIHandler):
237
236
  try:
238
237
  search_results = await self.db_manager.search_file_descriptions(
239
238
  project_id=project.id,
240
- branch=project_info["branch"],
241
239
  query=search_term,
242
240
  max_results=max_file_results
243
241
  )
@@ -322,9 +320,8 @@ class DeepAskHandler(ClaudeAPIHandler):
322
320
  ) -> str:
323
321
  """Build stage 1 prompt for extracting search terms."""
324
322
  project_name = project_info["projectName"]
325
- branch = project_info.get("branch", "unknown")
326
323
 
327
- return f"""I need to answer a question about the codebase "{project_name}" (branch: {branch}). To provide the best answer, I need to search for relevant files and then answer the question.
324
+ return f"""I need to answer a question about the codebase "{project_name}". To provide the best answer, I need to search for relevant files and then answer the question.
328
325
 
329
326
  PROJECT OVERVIEW:
330
327
  {overview}
@@ -352,7 +349,6 @@ Respond with valid JSON in this format:
352
349
  ) -> str:
353
350
  """Build stage 2 prompt for enhanced answer."""
354
351
  project_name = project_info["projectName"]
355
- branch = project_info.get("branch", "unknown")
356
352
 
357
353
  # Format file descriptions
358
354
  file_context = ""
@@ -365,7 +361,7 @@ Respond with valid JSON in this format:
365
361
  else:
366
362
  file_context = "\n\nNo relevant files found in the search."
367
363
 
368
- return f"""Please answer the following question about the codebase "{project_name}" (branch: {branch}).
364
+ return f"""Please answer the following question about the codebase "{project_name}".
369
365
 
370
366
  PROJECT OVERVIEW (COMPRESSED):
371
367
  {compressed_overview}
@@ -432,7 +428,7 @@ Your answer should be comprehensive but focused on the specific question asked."
432
428
 
433
429
  output = []
434
430
  output.append(f"Question: {result['question']}")
435
- output.append(f"Project: {result['project_name']} (branch: {metadata['branch']})")
431
+ output.append(f"Project: {result['project_name']}")
436
432
  output.append("")
437
433
  output.append("Answer:")
438
434
  output.append(answer)
@@ -307,17 +307,12 @@ Return ONLY a JSON object:
307
307
  except subprocess.CalledProcessError:
308
308
  pass # No upstream remote
309
309
 
310
- # Get current branch
311
- branch_result = await self._run_git_command(["rev-parse", "--abbrev-ref", "HEAD"])
312
- branch = branch_result.strip() if branch_result else "main"
313
-
314
310
  # Extract project name from remote URL or use directory name
315
311
  project_name = self._extract_project_name(remote_origin, project_root)
316
312
 
317
313
  return {
318
314
  "projectName": project_name,
319
315
  "folderPath": str(project_root),
320
- "branch": branch,
321
316
  "remoteOrigin": remote_origin,
322
317
  "upstreamOrigin": upstream_origin
323
318
  }
@@ -515,7 +510,7 @@ Return ONLY a JSON object:
515
510
 
516
511
  if project:
517
512
  overview = await self.db_manager.get_project_overview(
518
- project.id, project_info["branch"]
513
+ project.id
519
514
  )
520
515
  return overview.overview if overview else ""
521
516
 
@@ -538,7 +533,7 @@ Return ONLY a JSON object:
538
533
 
539
534
  if project:
540
535
  descriptions = await self.db_manager.get_all_file_descriptions(
541
- project.id, project_info["branch"]
536
+ project.id
542
537
  )
543
538
  return {desc.file_path: desc.description for desc in descriptions}
544
539
 
@@ -883,7 +878,6 @@ Return ONLY a JSON object:
883
878
 
884
879
  file_desc = FileDescription(
885
880
  project_id=project.id,
886
- branch=project_info["branch"],
887
881
  file_path=file_path,
888
882
  description=description,
889
883
  file_hash=None,
@@ -901,7 +895,6 @@ Return ONLY a JSON object:
901
895
 
902
896
  overview = ProjectOverview(
903
897
  project_id=project.id,
904
- branch=project_info["branch"],
905
898
  overview=overview_update,
906
899
  last_modified=datetime.utcnow(),
907
900
  total_files=len(file_updates),
mcp_code_indexer/main.py CHANGED
@@ -294,6 +294,7 @@ async def handle_runcommand(args: argparse.Namespace) -> None:
294
294
  "update_codebase_overview": server._handle_update_codebase_overview,
295
295
  "get_word_frequency": server._handle_get_word_frequency,
296
296
  "merge_branch_descriptions": server._handle_merge_branch_descriptions,
297
+ "search_codebase_overview": server._handle_search_codebase_overview,
297
298
  }
298
299
 
299
300
  if tool_name not in tool_handlers: