mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
fix: prevent nested project paths to avoid data conflicts (#338)
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,40 @@ class ProjectService:
|
||||
name
|
||||
)
|
||||
|
||||
def _check_nested_paths(self, path1: str, path2: str) -> bool:
|
||||
"""Check if two paths are nested (one is a prefix of the other).
|
||||
|
||||
Args:
|
||||
path1: First path to compare
|
||||
path2: Second path to compare
|
||||
|
||||
Returns:
|
||||
True if one path is nested within the other, False otherwise
|
||||
|
||||
Examples:
|
||||
_check_nested_paths("/foo", "/foo/bar") # True (child under parent)
|
||||
_check_nested_paths("/foo/bar", "/foo") # True (parent over child)
|
||||
_check_nested_paths("/foo", "/bar") # False (siblings)
|
||||
"""
|
||||
# Normalize paths to ensure proper comparison
|
||||
p1 = Path(path1).resolve()
|
||||
p2 = Path(path2).resolve()
|
||||
|
||||
# Check if either path is a parent of the other
|
||||
try:
|
||||
# Check if p2 is under p1
|
||||
p2.relative_to(p1)
|
||||
return True
|
||||
except ValueError:
|
||||
# Not nested in this direction, check the other
|
||||
try:
|
||||
# Check if p1 is under p2
|
||||
p1.relative_to(p2)
|
||||
return True
|
||||
except ValueError:
|
||||
# Not nested in either direction
|
||||
return False
|
||||
|
||||
async def add_project(self, name: str, path: str, set_default: bool = False) -> None:
|
||||
"""Add a new project to the configuration and database.
|
||||
|
||||
@@ -142,6 +176,29 @@ class ProjectService:
|
||||
else:
|
||||
resolved_path = Path(os.path.abspath(os.path.expanduser(path))).as_posix()
|
||||
|
||||
# Check for nested paths with existing projects
|
||||
existing_projects = await self.list_projects()
|
||||
for existing in existing_projects:
|
||||
if self._check_nested_paths(resolved_path, existing.path):
|
||||
# Determine which path is nested within which for appropriate error message
|
||||
p_new = Path(resolved_path).resolve()
|
||||
p_existing = Path(existing.path).resolve()
|
||||
|
||||
# Check if new path is nested under existing project
|
||||
if p_new.is_relative_to(p_existing):
|
||||
raise ValueError(
|
||||
f"Cannot create project at '{resolved_path}': "
|
||||
f"path is nested within existing project '{existing.name}' at '{existing.path}'. "
|
||||
f"Projects cannot share directory trees."
|
||||
)
|
||||
else:
|
||||
# Existing project is nested under new path
|
||||
raise ValueError(
|
||||
f"Cannot create project at '{resolved_path}': "
|
||||
f"existing project '{existing.name}' at '{existing.path}' is nested within this path. "
|
||||
f"Projects cannot share directory trees."
|
||||
)
|
||||
|
||||
# First add to config file (this will validate the project doesn't exist)
|
||||
project_config = self.config_manager.add_project(name, resolved_path)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user