diff --git a/nexus/mnemosyne/archive.py b/nexus/mnemosyne/archive.py index a84a676..4d719c0 100644 --- a/nexus/mnemosyne/archive.py +++ b/nexus/mnemosyne/archive.py @@ -212,6 +212,79 @@ class MnemosyneArchive: def count(self) -> int: return len(self._entries) + + + def add_tags(self, entry_id: str, tags: list[str]) -> list[str]: + """Add tags to an existing entry. Returns the entry's full topic list after addition. + + Args: + entry_id: The entry ID to modify. + tags: List of tags to add (duplicates are ignored). + + Raises: + KeyError: If entry_id does not exist. + """ + entry = self._entries.get(entry_id) + if entry is None: + raise KeyError(f"Entry not found: {entry_id}") + + existing_lower = {t.lower() for t in entry.topics} + for tag in tags: + tag = tag.strip() + if tag and tag.lower() not in existing_lower: + entry.topics.append(tag) + existing_lower.add(tag.lower()) + + self._save() + return entry.topics + + def remove_tags(self, entry_id: str, tags: list[str]) -> list[str]: + """Remove tags from an existing entry. Returns the entry's topic list after removal. + + Args: + entry_id: The entry ID to modify. + tags: List of tags to remove (case-insensitive). + + Raises: + KeyError: If entry_id does not exist. + """ + entry = self._entries.get(entry_id) + if entry is None: + raise KeyError(f"Entry not found: {entry_id}") + + remove_lower = {t.strip().lower() for t in tags} + entry.topics = [t for t in entry.topics if t.lower() not in remove_lower] + + self._save() + return entry.topics + + def retag(self, entry_id: str, tags: list[str]) -> list[str]: + """Replace all tags on an existing entry. Returns the new topic list. + + Args: + entry_id: The entry ID to modify. + tags: The new complete tag list (replaces all existing tags). + + Raises: + KeyError: If entry_id does not exist. + """ + entry = self._entries.get(entry_id) + if entry is None: + raise KeyError(f"Entry not found: {entry_id}") + + # Deduplicate, preserve order + seen: set[str] = set() + deduped: list[str] = [] + for tag in tags: + tag = tag.strip() + if tag and tag.lower() not in seen: + deduped.append(tag) + seen.add(tag.lower()) + + entry.topics = deduped + self._save() + return entry.topics + def graph_data( self, topic_filter: Optional[str] = None,