Watch a directory for changes and auto-update the graph (blocking mode).
(
path: str,
context: Optional[str] = None,
use_polling: Optional[bool] = None,
sync_on_start: bool = False,
)
| 892 | |
| 893 | |
| 894 | def watch_helper( |
| 895 | path: str, |
| 896 | context: Optional[str] = None, |
| 897 | use_polling: Optional[bool] = None, |
| 898 | sync_on_start: bool = False, |
| 899 | ): |
| 900 | """Watch a directory for changes and auto-update the graph (blocking mode).""" |
| 901 | import logging |
| 902 | from ..core.watcher import CodeWatcher |
| 903 | |
| 904 | # Suppress verbose watchdog DEBUG logs |
| 905 | logging.getLogger('watchdog').setLevel(logging.WARNING) |
| 906 | logging.getLogger('watchdog.observers').setLevel(logging.WARNING) |
| 907 | logging.getLogger('watchdog.observers.inotify_buffer').setLevel(logging.WARNING) |
| 908 | |
| 909 | services = _initialize_services(context) |
| 910 | if not all(services[:3]): |
| 911 | _fail_services_init() |
| 912 | |
| 913 | db_manager, graph_builder, code_finder, ctx = services |
| 914 | path_obj = Path(path).resolve() |
| 915 | |
| 916 | if not path_obj.exists(): |
| 917 | console.print(f"[red]Error: Path does not exist: {path_obj}[/red]") |
| 918 | db_manager.close_driver() |
| 919 | raise typer.Exit(code=1) |
| 920 | |
| 921 | if not path_obj.is_dir(): |
| 922 | console.print(f"[red]Error: Path must be a directory: {path_obj}[/red]") |
| 923 | db_manager.close_driver() |
| 924 | raise typer.Exit(code=1) |
| 925 | |
| 926 | console.print(f"[bold cyan]🔍 Watching {path_obj} for changes...[/bold cyan]") |
| 927 | |
| 928 | # Check if already indexed — use File node count as a robust fallback so a |
| 929 | # transient empty result from list_indexed_repositories never triggers a |
| 930 | # destructive full rescan of an already-populated graph. |
| 931 | indexed_repos = code_finder.list_indexed_repositories() |
| 932 | is_indexed = any_repo_matches_path(indexed_repos, path_obj) |
| 933 | if not is_indexed: |
| 934 | # Fallback: count File nodes whose path starts with this repo's path. |
| 935 | # If > 100 exist, the repo is clearly already indexed — skip the scan. |
| 936 | try: |
| 937 | with code_finder.driver.session() as _s: |
| 938 | _r = _s.run( |
| 939 | "MATCH (n:File) WHERE n.path STARTS WITH $p RETURN count(n) AS c", |
| 940 | p=path_obj.as_posix() + "/" |
| 941 | ) |
| 942 | _count = _r.single()["c"] |
| 943 | if _count > 100: |
| 944 | info_logger( |
| 945 | f"[watch] list_indexed_repositories returned no match for {path_obj} " |
| 946 | f"but {_count} File nodes exist — treating as already indexed." |
| 947 | ) |
| 948 | is_indexed = True |
| 949 | except Exception as _e: |
| 950 | warning_logger(f"[watch] Fallback indexed check failed: {_e}") |
| 951 |
no test coverage detected