88from typing import List , Optional , Set , Sequence
99
1010from basic_memory .config import BasicMemoryConfig , WATCH_STATUS_JSON
11+ from basic_memory .ignore_utils import load_gitignore_patterns , should_ignore_path
1112from basic_memory .models import Project
1213from basic_memory .repository import ProjectRepository
1314from loguru import logger
@@ -82,6 +83,7 @@ def __init__(
8283 self .state = WatchServiceState ()
8384 self .status_path = Path .home () / ".basic-memory" / WATCH_STATUS_JSON
8485 self .status_path .parent .mkdir (parents = True , exist_ok = True )
86+ self ._ignore_patterns_cache : dict [Path , Set [str ]] = {}
8587
8688 # quiet mode for mcp so it doesn't mess up stdout
8789 self .console = Console (quiet = quiet )
@@ -91,6 +93,12 @@ async def _schedule_restart(self, stop_event: asyncio.Event):
9193 await asyncio .sleep (self .app_config .watch_project_reload_interval )
9294 stop_event .set ()
9395
96+ def _get_ignore_patterns (self , project_path : Path ) -> Set [str ]:
97+ """Get or load ignore patterns for a project path."""
98+ if project_path not in self ._ignore_patterns_cache :
99+ self ._ignore_patterns_cache [project_path ] = load_gitignore_patterns (project_path )
100+ return self ._ignore_patterns_cache [project_path ]
101+
94102 async def _watch_projects_cycle (self , projects : Sequence [Project ], stop_event : asyncio .Event ):
95103 """Run one cycle of watching the given projects until stop_event is set."""
96104 project_paths = [project .path for project in projects ]
@@ -102,11 +110,20 @@ async def _watch_projects_cycle(self, projects: Sequence[Project], stop_event: a
102110 recursive = True ,
103111 stop_event = stop_event ,
104112 ):
105- # group changes by project
113+ # group changes by project and filter using ignore patterns
106114 project_changes = defaultdict (list )
107115 for change , path in changes :
108116 for project in projects :
109117 if self .is_project_path (project , path ):
118+ # Check if the file should be ignored based on gitignore patterns
119+ project_path = Path (project .path )
120+ file_path = Path (path )
121+ ignore_patterns = self ._get_ignore_patterns (project_path )
122+
123+ if should_ignore_path (file_path , project_path , ignore_patterns ):
124+ logger .trace (f"Ignoring watched file change: { file_path .relative_to (project_path )} " )
125+ continue
126+
110127 project_changes [project ].append ((change , path ))
111128 break
112129
@@ -134,6 +151,9 @@ async def run(self): # pragma: no cover
134151
135152 try :
136153 while self .state .running :
154+ # Clear ignore patterns cache to pick up any .gitignore changes
155+ self ._ignore_patterns_cache .clear ()
156+
137157 # Reload projects to catch any new/removed projects
138158 projects = await self .project_repository .get_active_projects ()
139159
0 commit comments