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.
@@ -32,7 +32,7 @@ from mcp_code_indexer.database.models import (
32
32
  from mcp_code_indexer.error_handler import setup_error_handling, ErrorHandler
33
33
  from mcp_code_indexer.middleware.error_middleware import create_tool_middleware, AsyncTaskManager
34
34
  from mcp_code_indexer.logging_config import get_logger
35
- from mcp_code_indexer.merge_handler import MergeHandler
35
+
36
36
 
37
37
  logger = logging.getLogger(__name__)
38
38
 
@@ -104,7 +104,6 @@ class MCPCodeIndexServer:
104
104
  retry_jitter=retry_jitter
105
105
  )
106
106
  self.token_counter = TokenCounter(token_limit)
107
- self.merge_handler = MergeHandler(self.db_manager)
108
107
 
109
108
  # Setup error handling
110
109
  self.logger = get_logger(__name__)
@@ -248,10 +247,7 @@ class MCPCodeIndexServer:
248
247
  "type": "string",
249
248
  "description": "Absolute path to the project folder on disk"
250
249
  },
251
- "branch": {
252
- "type": "string",
253
- "description": "Git branch name (e.g., 'main', 'develop')"
254
- },
250
+
255
251
  "remoteOrigin": {
256
252
  "type": "string",
257
253
  "description": "Git remote origin URL if available"
@@ -265,7 +261,7 @@ class MCPCodeIndexServer:
265
261
  "description": "Relative path to the file from project root"
266
262
  }
267
263
  },
268
- "required": ["projectName", "folderPath", "branch", "filePath"],
264
+ "required": ["projectName", "folderPath", "filePath"],
269
265
  "additionalProperties": False
270
266
  }
271
267
  ),
@@ -277,31 +273,29 @@ class MCPCodeIndexServer:
277
273
  "properties": {
278
274
  "projectName": {"type": "string", "description": "The name of the project"},
279
275
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
280
- "branch": {"type": "string", "description": "Git branch name"},
281
276
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
282
277
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
283
278
  "filePath": {"type": "string", "description": "Relative path to the file from project root"},
284
279
  "description": {"type": "string", "description": "Detailed description of the file's contents"},
285
280
  "fileHash": {"type": "string", "description": "SHA-256 hash of the file contents (optional)"}
286
281
  },
287
- "required": ["projectName", "folderPath", "branch", "filePath", "description"],
282
+ "required": ["projectName", "folderPath", "filePath", "description"],
288
283
  "additionalProperties": False
289
284
  }
290
285
  ),
291
286
  types.Tool(
292
287
  name="check_codebase_size",
293
- description="Checks the total token count of a codebase's file structure and descriptions - if you're in a git repo be sure to run `git rev-parse --abbrev-ref HEAD` to see what branch you're on before running this tool. Returns whether the codebase is 'large' and recommends using search instead of the full overview.",
288
+ description="Checks the total token count of a codebase's file structure and descriptions. Returns whether the codebase is 'large' and recommends using search instead of the full overview.",
294
289
  inputSchema={
295
290
  "type": "object",
296
291
  "properties": {
297
292
  "projectName": {"type": "string", "description": "The name of the project"},
298
293
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
299
- "branch": {"type": "string", "description": "Git branch name"},
300
294
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
301
295
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
302
296
  "tokenLimit": {"type": "integer", "description": "Optional token limit override (defaults to server configuration)"}
303
297
  },
304
- "required": ["projectName", "folderPath", "branch"],
298
+ "required": ["projectName", "folderPath"],
305
299
  "additionalProperties": False
306
300
  }
307
301
  ),
@@ -313,12 +307,11 @@ class MCPCodeIndexServer:
313
307
  "properties": {
314
308
  "projectName": {"type": "string", "description": "The name of the project"},
315
309
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
316
- "branch": {"type": "string", "description": "Git branch name"},
317
310
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
318
311
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
319
312
  "limit": {"type": "integer", "description": "Maximum number of missing files to return (optional)"}
320
313
  },
321
- "required": ["projectName", "folderPath", "branch"],
314
+ "required": ["projectName", "folderPath"],
322
315
  "additionalProperties": False
323
316
  }
324
317
  ),
@@ -330,13 +323,12 @@ class MCPCodeIndexServer:
330
323
  "properties": {
331
324
  "projectName": {"type": "string", "description": "The name of the project"},
332
325
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
333
- "branch": {"type": "string", "description": "Git branch to search in"},
334
326
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
335
327
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
336
328
  "query": {"type": "string", "description": "Search query (e.g., 'authentication middleware', 'database models')"},
337
329
  "maxResults": {"type": "integer", "default": 20, "description": "Maximum number of results to return"}
338
330
  },
339
- "required": ["projectName", "folderPath", "branch", "query"],
331
+ "required": ["projectName", "folderPath", "query"],
340
332
  "additionalProperties": False
341
333
  }
342
334
  ),
@@ -348,57 +340,25 @@ class MCPCodeIndexServer:
348
340
  "properties": {
349
341
  "projectName": {"type": "string", "description": "The name of the project"},
350
342
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
351
- "branch": {"type": "string", "description": "Git branch name"},
352
343
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
353
344
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"}
354
345
  },
355
- "required": ["projectName", "folderPath", "branch"],
356
- "additionalProperties": False
357
- }
358
- ),
359
- types.Tool(
360
- name="merge_branch_descriptions",
361
- description="Merges file descriptions from one branch to another. This is a two-stage process: first call without resolutions returns conflicts where the same file has different descriptions in each branch. Second call with resolutions completes the merge.",
362
- inputSchema={
363
- "type": "object",
364
- "properties": {
365
- "projectName": {"type": "string", "description": "The name of the project"},
366
- "folderPath": {"type": "string", "description": "Absolute path to the project folder"},
367
- "remoteOrigin": {"type": "string", "description": "Git remote origin URL"},
368
- "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
369
- "sourceBranch": {"type": "string", "description": "Branch to merge from (e.g., 'feature/new-ui')"},
370
- "targetBranch": {"type": "string", "description": "Branch to merge into (e.g., 'main')"},
371
- "conflictResolutions": {
372
- "type": ["array", "null"],
373
- "description": "Array of resolved conflicts (only for second stage)",
374
- "items": {
375
- "type": "object",
376
- "properties": {
377
- "conflictId": {"type": "string", "description": "ID of the conflict to resolve"},
378
- "resolvedDescription": {"type": "string", "description": "Final description to use after merge"}
379
- },
380
- "required": ["conflictId", "resolvedDescription"],
381
- "additionalProperties": False
382
- }
383
- }
384
- },
385
- "required": ["projectName", "folderPath", "sourceBranch", "targetBranch"],
346
+ "required": ["projectName", "folderPath"],
386
347
  "additionalProperties": False
387
348
  }
388
349
  ),
389
350
  types.Tool(
390
351
  name="get_codebase_overview",
391
- description="Returns a condensed, interpretive overview of the entire codebase - if you're in a git repo be sure to run `git rev-parse --abbrev-ref HEAD` to see what branch you're on before running this tool. This is a single comprehensive narrative that captures the architecture, key components, relationships, and design patterns. Unlike get_all_descriptions which lists every file, this provides a holistic view suitable for understanding the codebase's structure and purpose. If no overview exists, returns empty string.",
352
+ description="Returns a condensed, interpretive overview of the entire codebase. This is a single comprehensive narrative that captures the architecture, key components, relationships, and design patterns. Unlike get_all_descriptions which lists every file, this provides a holistic view suitable for understanding the codebase's structure and purpose. If no overview exists, returns empty string.",
392
353
  inputSchema={
393
354
  "type": "object",
394
355
  "properties": {
395
356
  "projectName": {"type": "string", "description": "The name of the project"},
396
357
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
397
- "branch": {"type": "string", "description": "Git branch name"},
398
358
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
399
359
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"}
400
360
  },
401
- "required": ["projectName", "folderPath", "branch"],
361
+ "required": ["projectName", "folderPath"],
402
362
  "additionalProperties": False
403
363
  }
404
364
  ),
@@ -444,29 +404,27 @@ src/
444
404
  "properties": {
445
405
  "projectName": {"type": "string", "description": "The name of the project"},
446
406
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
447
- "branch": {"type": "string", "description": "Git branch name"},
448
407
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
449
408
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
450
409
  "overview": {"type": "string", "description": "Comprehensive narrative overview of the codebase (10-30k tokens recommended)"}
451
410
  },
452
- "required": ["projectName", "folderPath", "branch", "overview"],
411
+ "required": ["projectName", "folderPath", "overview"],
453
412
  "additionalProperties": False
454
413
  }
455
414
  ),
456
415
  types.Tool(
457
416
  name="get_word_frequency",
458
- description="Analyzes all file descriptions to find the most frequently used technical terms - if you're in a git repo be sure to run `git rev-parse --abbrev-ref HEAD` to see what branch you're on before running this tool. Filters out common English stop words and symbols, returning the top 200 meaningful terms. Useful for understanding the codebase's domain vocabulary and finding all functions/files related to specific concepts.",
417
+ description="Analyzes all file descriptions to find the most frequently used technical terms. Filters out common English stop words and symbols, returning the top 200 meaningful terms. Useful for understanding the codebase's domain vocabulary and finding all functions/files related to specific concepts.",
459
418
  inputSchema={
460
419
  "type": "object",
461
420
  "properties": {
462
421
  "projectName": {"type": "string", "description": "The name of the project"},
463
422
  "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
464
- "branch": {"type": "string", "description": "Git branch name"},
465
423
  "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
466
424
  "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
467
425
  "limit": {"type": "integer", "default": 200, "description": "Number of top terms to return"}
468
426
  },
469
- "required": ["projectName", "folderPath", "branch"],
427
+ "required": ["projectName", "folderPath"],
470
428
  "additionalProperties": False
471
429
  }
472
430
  ),
@@ -478,6 +436,22 @@ src/
478
436
  "properties": {},
479
437
  "additionalProperties": False
480
438
  }
439
+ ),
440
+ types.Tool(
441
+ name="search_codebase_overview",
442
+ description="Search for a single word in the codebase overview and return 2 sentences before and after where the word is found. Useful for quickly finding specific information in large overviews.",
443
+ inputSchema={
444
+ "type": "object",
445
+ "properties": {
446
+ "projectName": {"type": "string", "description": "The name of the project"},
447
+ "folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
448
+ "remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
449
+ "upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
450
+ "searchWord": {"type": "string", "description": "Single word to search for in the overview"}
451
+ },
452
+ "required": ["projectName", "folderPath", "searchWord"],
453
+ "additionalProperties": False
454
+ }
481
455
  )
482
456
  ]
483
457
 
@@ -501,8 +475,9 @@ src/
501
475
  "get_codebase_overview": self._handle_get_condensed_overview,
502
476
  "update_codebase_overview": self._handle_update_codebase_overview,
503
477
  "get_word_frequency": self._handle_get_word_frequency,
504
- "merge_branch_descriptions": self._handle_merge_branch_descriptions,
478
+
505
479
  "check_database_health": self._handle_check_database_health,
480
+ "search_codebase_overview": self._handle_search_codebase_overview,
506
481
  }
507
482
 
508
483
  if name not in tool_handlers:
@@ -556,7 +531,7 @@ src/
556
531
  remote_origin = arguments.get("remoteOrigin")
557
532
  upstream_origin = arguments.get("upstreamOrigin")
558
533
  folder_path = arguments["folderPath"]
559
- branch = arguments.get("branch", "main")
534
+
560
535
 
561
536
  # Normalize project name for case-insensitive matching
562
537
  normalized_name = project_name.lower()
@@ -572,7 +547,7 @@ src/
572
547
  # Check if upstream inheritance is needed
573
548
  if upstream_origin and await self.db_manager.check_upstream_inheritance_needed(project):
574
549
  try:
575
- inherited_count = await self.db_manager.inherit_from_upstream(project, branch)
550
+ inherited_count = await self.db_manager.inherit_from_upstream(project)
576
551
  if inherited_count > 0:
577
552
  logger.info(f"Auto-inherited {inherited_count} descriptions from upstream for {normalized_name}")
578
553
  except Exception as e:
@@ -595,7 +570,7 @@ src/
595
570
  # Auto-inherit from upstream if needed
596
571
  if upstream_origin:
597
572
  try:
598
- inherited_count = await self.db_manager.inherit_from_upstream(project, branch)
573
+ inherited_count = await self.db_manager.inherit_from_upstream(project)
599
574
  if inherited_count > 0:
600
575
  logger.info(f"Auto-inherited {inherited_count} descriptions from upstream for {normalized_name}")
601
576
  except Exception as e:
@@ -689,7 +664,7 @@ src/
689
664
  return False
690
665
 
691
666
  # Get files already indexed for this project
692
- indexed_files = await self.db_manager.get_all_file_descriptions(project.id, "main")
667
+ indexed_files = await self.db_manager.get_all_file_descriptions(project.id)
693
668
  indexed_basenames = {Path(fd.file_path).name for fd in indexed_files}
694
669
 
695
670
  if not indexed_basenames:
@@ -746,65 +721,14 @@ src/
746
721
  await self.db_manager.update_project(project)
747
722
  logger.debug(f"Updated project metadata for {project.name}")
748
723
 
749
- async def _resolve_branch(self, project_id: str, requested_branch: str) -> str:
750
- """
751
- Resolve the actual branch to use for operations.
752
-
753
- For new projects or branches with no existing files, uses the requested branch.
754
- For existing projects, tries to find a consistent branch to avoid data fragmentation.
755
-
756
- Returns the resolved branch name.
757
- """
758
- try:
759
- # Get all branches and their file counts for this project
760
- branch_counts = await self.db_manager.get_branch_file_counts(project_id)
761
-
762
- # If no existing data, use the requested branch
763
- if not branch_counts:
764
- return requested_branch
765
-
766
- # If requested branch has files, use it
767
- if requested_branch in branch_counts and branch_counts[requested_branch] > 0:
768
- return requested_branch
769
-
770
- # Try common branch name variations to find existing data
771
- common_variations = {
772
- 'main': ['master', 'develop', 'development', 'dev'],
773
- 'master': ['main', 'develop', 'development', 'dev'],
774
- 'develop': ['development', 'main', 'master', 'dev'],
775
- 'development': ['develop', 'main', 'master', 'dev'],
776
- 'dev': ['develop', 'development', 'main', 'master']
777
- }
778
-
779
- # Try variations of the requested branch
780
- if requested_branch.lower() in common_variations:
781
- for variation in common_variations[requested_branch.lower()]:
782
- if variation in branch_counts and branch_counts[variation] > 0:
783
- logger.info(f"Resolved branch '{requested_branch}' to existing branch '{variation}' with {branch_counts[variation]} files")
784
- return variation
785
-
786
- # If no variations found, check if we should use the main data branch
787
- # (to avoid fragmenting data across multiple branches)
788
- best_branch = max(branch_counts.items(), key=lambda x: x[1])
789
- if best_branch[1] > 10: # Only if there's substantial existing data
790
- logger.info(f"Using primary branch '{best_branch[0]}' instead of '{requested_branch}' to avoid data fragmentation")
791
- return best_branch[0]
792
-
793
- # Fall back to requested branch for new/small projects
794
- return requested_branch
795
-
796
- except Exception as e:
797
- logger.warning(f"Error resolving branch: {e}")
798
- return requested_branch
724
+
799
725
 
800
726
  async def _handle_get_file_description(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
801
727
  """Handle get_file_description tool calls."""
802
728
  project_id = await self._get_or_create_project_id(arguments)
803
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
804
729
 
805
730
  file_desc = await self.db_manager.get_file_description(
806
731
  project_id=project_id,
807
- branch=resolved_branch,
808
732
  file_path=arguments["filePath"]
809
733
  )
810
734
 
@@ -826,19 +750,16 @@ src/
826
750
  """Handle update_file_description tool calls."""
827
751
  logger.info(f"Updating file description for: {arguments['filePath']}")
828
752
  logger.info(f"Project: {arguments.get('projectName', 'Unknown')}")
829
- logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
830
753
 
831
754
  description_length = len(arguments.get("description", ""))
832
755
  logger.info(f"Description length: {description_length} characters")
833
756
 
834
757
  project_id = await self._get_or_create_project_id(arguments)
835
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
836
758
 
837
- logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
759
+ logger.info(f"Resolved project_id: {project_id}")
838
760
 
839
761
  file_desc = FileDescription(
840
762
  project_id=project_id,
841
- branch=resolved_branch,
842
763
  file_path=arguments["filePath"],
843
764
  description=arguments["description"],
844
765
  file_hash=arguments.get("fileHash"),
@@ -861,46 +782,52 @@ src/
861
782
  """Handle check_codebase_size tool calls."""
862
783
  logger.info(f"Checking codebase size for: {arguments.get('projectName', 'Unknown')}")
863
784
  logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
864
- logger.info(f"Branch: {arguments.get('branch', 'Unknown')}")
865
785
 
866
786
  project_id = await self._get_or_create_project_id(arguments)
867
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
868
787
  folder_path = Path(arguments["folderPath"])
869
788
 
870
- logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
789
+ logger.info(f"Resolved project_id: {project_id}")
871
790
 
872
791
  # Clean up descriptions for files that no longer exist
873
792
  logger.info("Cleaning up descriptions for missing files...")
874
793
  cleaned_up_files = await self.db_manager.cleanup_missing_files(
875
794
  project_id=project_id,
876
- branch=resolved_branch,
877
795
  project_root=folder_path
878
796
  )
879
797
  logger.info(f"Cleaned up {len(cleaned_up_files)} missing files")
880
798
 
881
- # Get file descriptions for this project/branch (after cleanup)
799
+ # Get file descriptions for this project (after cleanup)
882
800
  logger.info("Retrieving file descriptions...")
883
801
  file_descriptions = await self.db_manager.get_all_file_descriptions(
884
- project_id=project_id,
885
- branch=resolved_branch
802
+ project_id=project_id
886
803
  )
887
804
  logger.info(f"Found {len(file_descriptions)} file descriptions")
888
805
 
889
806
  # Use provided token limit or fall back to server default
890
807
  token_limit = arguments.get("tokenLimit", self.token_limit)
891
808
 
892
- # Calculate total tokens
809
+ # Calculate total tokens for descriptions
893
810
  logger.info("Calculating total token count...")
894
- total_tokens = self.token_counter.calculate_codebase_tokens(file_descriptions)
811
+ descriptions_tokens = self.token_counter.calculate_codebase_tokens(file_descriptions)
812
+
813
+ # Get overview tokens if available
814
+ overview = await self.db_manager.get_project_overview(project_id)
815
+ overview_tokens = 0
816
+ if overview and overview.overview:
817
+ overview_tokens = self.token_counter.count_tokens(overview.overview)
818
+
819
+ total_tokens = descriptions_tokens + overview_tokens
895
820
  is_large = total_tokens > token_limit
896
821
  recommendation = "use_search" if is_large else "use_overview"
897
822
 
898
- logger.info(f"Codebase analysis complete: {total_tokens} tokens, {len(file_descriptions)} files")
823
+ logger.info(f"Codebase analysis complete: {total_tokens} tokens total ({descriptions_tokens} descriptions + {overview_tokens} overview), {len(file_descriptions)} files")
899
824
  logger.info(f"Size assessment: {'LARGE' if is_large else 'SMALL'} (limit: {token_limit})")
900
825
  logger.info(f"Recommendation: {recommendation}")
901
826
 
902
827
  return {
903
828
  "totalTokens": total_tokens,
829
+ "descriptionsTokens": descriptions_tokens,
830
+ "overviewTokens": overview_tokens,
904
831
  "isLarge": is_large,
905
832
  "recommendation": recommendation,
906
833
  "tokenLimit": token_limit,
@@ -915,16 +842,14 @@ src/
915
842
  logger.info(f"Folder path: {arguments.get('folderPath', 'Unknown')}")
916
843
 
917
844
  project_id = await self._get_or_create_project_id(arguments)
918
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
919
845
  folder_path = Path(arguments["folderPath"])
920
846
 
921
- logger.info(f"Resolved project_id: {project_id}, branch: {resolved_branch}")
847
+ logger.info(f"Resolved project_id: {project_id}")
922
848
 
923
849
  # Get existing file descriptions
924
850
  logger.info("Retrieving existing file descriptions...")
925
851
  existing_descriptions = await self.db_manager.get_all_file_descriptions(
926
- project_id=project_id,
927
- branch=resolved_branch
852
+ project_id=project_id
928
853
  )
929
854
  existing_paths = {desc.file_path for desc in existing_descriptions}
930
855
  logger.info(f"Found {len(existing_paths)} existing descriptions")
@@ -965,13 +890,11 @@ src/
965
890
  async def _handle_search_descriptions(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
966
891
  """Handle search_descriptions tool calls."""
967
892
  project_id = await self._get_or_create_project_id(arguments)
968
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
969
893
  max_results = arguments.get("maxResults", 20)
970
894
 
971
895
  # Perform search
972
896
  search_results = await self.db_manager.search_file_descriptions(
973
897
  project_id=project_id,
974
- branch=resolved_branch,
975
898
  query=arguments["query"],
976
899
  max_results=max_results
977
900
  )
@@ -995,12 +918,10 @@ src/
995
918
  async def _handle_get_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
996
919
  """Handle get_codebase_overview tool calls."""
997
920
  project_id = await self._get_or_create_project_id(arguments)
998
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
999
921
 
1000
922
  # Get all file descriptions
1001
923
  file_descriptions = await self.db_manager.get_all_file_descriptions(
1002
- project_id=project_id,
1003
- branch=resolved_branch
924
+ project_id=project_id
1004
925
  )
1005
926
 
1006
927
  # Calculate total tokens
@@ -1012,8 +933,6 @@ src/
1012
933
 
1013
934
  return {
1014
935
  "projectName": arguments["projectName"],
1015
- "branch": resolved_branch,
1016
- "requestedBranch": arguments["branch"] if arguments["branch"] != resolved_branch else None,
1017
936
  "totalFiles": len(file_descriptions),
1018
937
  "totalTokens": total_tokens,
1019
938
  "isLarge": is_large,
@@ -1064,77 +983,14 @@ src/
1064
983
 
1065
984
  return convert_structure(root)
1066
985
 
1067
- async def _handle_merge_branch_descriptions(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1068
- """Handle merge_branch_descriptions tool calls."""
1069
- project_id = await self._get_or_create_project_id(arguments)
1070
- source_branch = arguments["sourceBranch"]
1071
- target_branch = arguments["targetBranch"]
1072
- conflict_resolutions = arguments.get("conflictResolutions")
1073
-
1074
- if conflict_resolutions is None:
1075
- # Phase 1: Detect conflicts
1076
- session = await self.merge_handler.start_merge_phase1(
1077
- project_id, source_branch, target_branch
1078
- )
1079
-
1080
- if session.get_conflict_count() == 0:
1081
- # No conflicts, can merge immediately
1082
- return {
1083
- "phase": "completed",
1084
- "conflicts": [],
1085
- "message": f"No conflicts detected. Merge from {source_branch} to {target_branch} can proceed automatically.",
1086
- "sourceBranch": source_branch,
1087
- "targetBranch": target_branch,
1088
- "conflictCount": 0
1089
- }
1090
- else:
1091
- # Return conflicts for resolution
1092
- return {
1093
- "phase": "conflicts_detected",
1094
- "sessionId": session.session_id,
1095
- "conflicts": [conflict.to_dict() for conflict in session.conflicts],
1096
- "conflictCount": session.get_conflict_count(),
1097
- "sourceBranch": source_branch,
1098
- "targetBranch": target_branch,
1099
- "message": f"Found {session.get_conflict_count()} conflicts that need resolution."
1100
- }
1101
- else:
1102
- # Phase 2: Apply resolutions
1103
- # Find the session ID from conflict resolutions
1104
- if not conflict_resolutions:
1105
- from ..error_handler import ValidationError
1106
- raise ValidationError("Conflict resolutions required for phase 2")
1107
-
1108
- # For simplicity, create a new session and resolve immediately
1109
- # In a production system, you'd want to track session IDs properly
1110
- session = await self.merge_handler.start_merge_phase1(
1111
- project_id, source_branch, target_branch
1112
- )
1113
-
1114
- if session.get_conflict_count() == 0:
1115
- return {
1116
- "phase": "completed",
1117
- "message": "No conflicts to resolve",
1118
- "sourceBranch": source_branch,
1119
- "targetBranch": target_branch
1120
- }
1121
-
1122
- result = await self.merge_handler.complete_merge_phase2(
1123
- session.session_id, conflict_resolutions
1124
- )
1125
-
1126
- return {
1127
- "phase": "completed",
1128
- **result
1129
- }
986
+
1130
987
 
1131
988
  async def _handle_get_condensed_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1132
989
  """Handle get_codebase_overview tool calls for condensed overviews."""
1133
990
  project_id = await self._get_or_create_project_id(arguments)
1134
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
1135
991
 
1136
992
  # Try to get existing overview
1137
- overview = await self.db_manager.get_project_overview(project_id, resolved_branch)
993
+ overview = await self.db_manager.get_project_overview(project_id)
1138
994
 
1139
995
  if overview:
1140
996
  return {
@@ -1154,13 +1010,11 @@ src/
1154
1010
  async def _handle_update_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1155
1011
  """Handle update_codebase_overview tool calls."""
1156
1012
  project_id = await self._get_or_create_project_id(arguments)
1157
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
1158
1013
  folder_path = Path(arguments["folderPath"])
1159
1014
 
1160
1015
  # Get current file count and total tokens for context
1161
1016
  file_descriptions = await self.db_manager.get_all_file_descriptions(
1162
- project_id=project_id,
1163
- branch=resolved_branch
1017
+ project_id=project_id
1164
1018
  )
1165
1019
 
1166
1020
  total_files = len(file_descriptions)
@@ -1169,7 +1023,6 @@ src/
1169
1023
  # Create overview record
1170
1024
  overview = ProjectOverview(
1171
1025
  project_id=project_id,
1172
- branch=resolved_branch,
1173
1026
  overview=arguments["overview"],
1174
1027
  last_modified=datetime.utcnow(),
1175
1028
  total_files=total_files,
@@ -1189,13 +1042,11 @@ src/
1189
1042
  async def _handle_get_word_frequency(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1190
1043
  """Handle get_word_frequency tool calls."""
1191
1044
  project_id = await self._get_or_create_project_id(arguments)
1192
- resolved_branch = await self._resolve_branch(project_id, arguments["branch"])
1193
1045
  limit = arguments.get("limit", 200)
1194
1046
 
1195
1047
  # Analyze word frequency
1196
1048
  result = await self.db_manager.analyze_word_frequency(
1197
1049
  project_id=project_id,
1198
- branch=resolved_branch,
1199
1050
  limit=limit
1200
1051
  )
1201
1052
 
@@ -1205,6 +1056,53 @@ src/
1205
1056
  "totalUniqueTerms": result.total_unique_terms
1206
1057
  }
1207
1058
 
1059
+ async def _handle_search_codebase_overview(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1060
+ """Handle search_codebase_overview tool calls."""
1061
+ project_id = await self._get_or_create_project_id(arguments)
1062
+ search_word = arguments["searchWord"].lower()
1063
+
1064
+ # Get the overview
1065
+ overview = await self.db_manager.get_project_overview(project_id)
1066
+
1067
+ if not overview or not overview.overview:
1068
+ return {
1069
+ "found": False,
1070
+ "message": "No overview found for this project",
1071
+ "searchWord": arguments["searchWord"]
1072
+ }
1073
+
1074
+ # Split overview into sentences
1075
+ import re
1076
+ sentences = re.split(r'[.!?]+', overview.overview)
1077
+ sentences = [s.strip() for s in sentences if s.strip()]
1078
+
1079
+ # Find matches
1080
+ matches = []
1081
+ for i, sentence in enumerate(sentences):
1082
+ if search_word in sentence.lower():
1083
+ # Get context: 2 sentences before and after
1084
+ start_idx = max(0, i - 2)
1085
+ end_idx = min(len(sentences), i + 3)
1086
+
1087
+ context_sentences = sentences[start_idx:end_idx]
1088
+ context = '. '.join(context_sentences) + '.'
1089
+
1090
+ matches.append({
1091
+ "matchIndex": i,
1092
+ "matchSentence": sentence,
1093
+ "context": context,
1094
+ "contextStartIndex": start_idx,
1095
+ "contextEndIndex": end_idx - 1
1096
+ })
1097
+
1098
+ return {
1099
+ "found": len(matches) > 0,
1100
+ "searchWord": arguments["searchWord"],
1101
+ "matches": matches,
1102
+ "totalMatches": len(matches),
1103
+ "totalSentences": len(sentences)
1104
+ }
1105
+
1208
1106
  async def _handle_check_database_health(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
1209
1107
  """
1210
1108
  Handle check_database_health tool calls with comprehensive diagnostics.