Watch a directory for changes and auto-update the graph (blocking mode).
(path: str, context: Optional[str] = None)
| 690 | |
| 691 | |
| 692 | def watch_helper(path: str, context: Optional[str] = None): |
| 693 | """Watch a directory for changes and auto-update the graph (blocking mode).""" |
| 694 | import logging |
| 695 | from ..core.watcher import CodeWatcher |
| 696 | |
| 697 | # Suppress verbose watchdog DEBUG logs |
| 698 | logging.getLogger('watchdog').setLevel(logging.WARNING) |
| 699 | logging.getLogger('watchdog.observers').setLevel(logging.WARNING) |
| 700 | logging.getLogger('watchdog.observers.inotify_buffer').setLevel(logging.WARNING) |
| 701 | |
| 702 | services = _initialize_services(context) |
| 703 | if not all(services[:3]): |
| 704 | return |
| 705 | |
| 706 | db_manager, graph_builder, code_finder, ctx = services |
| 707 | path_obj = Path(path).resolve() |
| 708 | |
| 709 | if not path_obj.exists(): |
| 710 | console.print(f"[red]Error: Path does not exist: {path_obj}[/red]") |
| 711 | db_manager.close_driver() |
| 712 | return |
| 713 | |
| 714 | if not path_obj.is_dir(): |
| 715 | console.print(f"[red]Error: Path must be a directory: {path_obj}[/red]") |
| 716 | db_manager.close_driver() |
| 717 | return |
| 718 | |
| 719 | console.print(f"[bold cyan]🔍 Watching {path_obj} for changes...[/bold cyan]") |
| 720 | |
| 721 | # Check if already indexed — use File node count as a robust fallback so a |
| 722 | # transient empty result from list_indexed_repositories never triggers a |
| 723 | # destructive full rescan of an already-populated graph. |
| 724 | indexed_repos = code_finder.list_indexed_repositories() |
| 725 | is_indexed = any_repo_matches_path(indexed_repos, path_obj) |
| 726 | if not is_indexed: |
| 727 | # Fallback: count File nodes whose path starts with this repo's path. |
| 728 | # If > 100 exist, the repo is clearly already indexed — skip the scan. |
| 729 | try: |
| 730 | with code_finder.driver.session() as _s: |
| 731 | _r = _s.run( |
| 732 | "MATCH (n:File) WHERE n.path STARTS WITH $p RETURN count(n) AS c", |
| 733 | p=str(path_obj) + "/" |
| 734 | ) |
| 735 | _count = _r.single()["c"] |
| 736 | if _count > 100: |
| 737 | info_logger( |
| 738 | f"[watch] list_indexed_repositories returned no match for {path_obj} " |
| 739 | f"but {_count} File nodes exist — treating as already indexed." |
| 740 | ) |
| 741 | is_indexed = True |
| 742 | except Exception as _e: |
| 743 | warning_logger(f"[watch] Fallback indexed check failed: {_e}") |
| 744 | |
| 745 | # Create watcher instance |
| 746 | job_manager = JobManager() |
| 747 | watcher = CodeWatcher(graph_builder, job_manager) |
| 748 | |
| 749 | try: |
no test coverage detected