Files
trace/trace/models/__init__.py
Claude b6387f4b0c Restructure codebase for AI agent optimization
Major refactoring to organize code into focused, single-responsibility modules
that are easier for AI coding agents and developers to navigate and modify.

**Module Reorganization:**

Models Package (trace/models/):
- Moved models.py content into models/__init__.py
- Extracted IOC extraction into models/extractors/ioc_extractor.py (236 lines)
- Extracted tag extraction into models/extractors/tag_extractor.py (34 lines)
- Reduced duplication and improved maintainability

Storage Package (trace/storage_impl/):
- Split storage.py (402 lines) into focused modules:
  - storage.py: Main Storage class (112 lines)
  - state_manager.py: StateManager for context/settings (92 lines)
  - lock_manager.py: Cross-platform file locking (87 lines)
  - demo_data.py: Demo case creation (143 lines)
- Added backward-compatible wrapper at trace/storage.py

TUI Utilities (trace/tui/):
- Created rendering package:
  - colors.py: Color pair constants and initialization (43 lines)
  - text_renderer.py: Text rendering with highlighting (137 lines)
- Created handlers package:
  - export_handler.py: Export functionality (238 lines)
- Main tui.py (3307 lines) remains for future refactoring

**Benefits:**
- Smaller, focused files (most < 250 lines)
- Clear single responsibilities
- Easier to locate and modify specific functionality
- Better separation of concerns
- Reduced cognitive load for AI agents
- All tests pass, no features removed

**Testing:**
- All existing tests pass
- Imports verified
- CLI and storage functionality tested
- Backward compatibility maintained

Updated CLAUDE.md to document new architecture and AI optimization strategy.
2025-12-13 17:38:53 +00:00

132 lines
4.1 KiB
Python

"""Data models for trace application"""
import time
import hashlib
import uuid
from dataclasses import dataclass, field
from typing import List, Optional, Dict
from .extractors import TagExtractor, IOCExtractor
@dataclass
class Note:
content: str
timestamp: float = field(default_factory=time.time)
note_id: str = field(default_factory=lambda: str(uuid.uuid4()))
content_hash: str = ""
signature: Optional[str] = None
tags: List[str] = field(default_factory=list)
iocs: List[str] = field(default_factory=list)
def extract_tags(self):
"""Extract hashtags from content (case-insensitive, stored lowercase)"""
self.tags = TagExtractor.extract_tags(self.content)
def extract_iocs(self):
"""Extract Indicators of Compromise from content"""
self.iocs = IOCExtractor.extract_iocs(self.content)
def calculate_hash(self):
# We hash the content + timestamp to ensure integrity of 'when' it was said
data = f"{self.timestamp}:{self.content}".encode('utf-8')
self.content_hash = hashlib.sha256(data).hexdigest()
@staticmethod
def extract_iocs_from_text(text):
"""Extract IOCs from text and return as list of (ioc, type) tuples"""
return IOCExtractor.extract_iocs_with_types(text)
@staticmethod
def extract_iocs_with_positions(text):
"""Extract IOCs with their positions for highlighting. Returns list of (text, start, end, type) tuples"""
return IOCExtractor.extract_iocs_with_positions(text)
def to_dict(self):
return {
"note_id": self.note_id,
"content": self.content,
"timestamp": self.timestamp,
"content_hash": self.content_hash,
"signature": self.signature,
"tags": self.tags,
"iocs": self.iocs
}
@staticmethod
def from_dict(data):
note = Note(
content=data["content"],
timestamp=data["timestamp"],
note_id=data["note_id"],
content_hash=data.get("content_hash", ""),
signature=data.get("signature"),
tags=data.get("tags", []),
iocs=data.get("iocs", [])
)
return note
@dataclass
class Evidence:
name: str
evidence_id: str = field(default_factory=lambda: str(uuid.uuid4()))
description: str = ""
metadata: Dict[str, str] = field(default_factory=dict)
notes: List[Note] = field(default_factory=list)
def to_dict(self):
return {
"evidence_id": self.evidence_id,
"name": self.name,
"description": self.description,
"metadata": self.metadata,
"notes": [n.to_dict() for n in self.notes]
}
@staticmethod
def from_dict(data):
ev = Evidence(
name=data["name"],
evidence_id=data["evidence_id"],
description=data.get("description", ""),
metadata=data.get("metadata", {})
)
ev.notes = [Note.from_dict(n) for n in data.get("notes", [])]
return ev
@dataclass
class Case:
case_number: str
case_id: str = field(default_factory=lambda: str(uuid.uuid4()))
name: str = ""
investigator: str = ""
evidence: List[Evidence] = field(default_factory=list)
notes: List[Note] = field(default_factory=list)
def to_dict(self):
return {
"case_id": self.case_id,
"case_number": self.case_number,
"name": self.name,
"investigator": self.investigator,
"evidence": [e.to_dict() for e in self.evidence],
"notes": [n.to_dict() for n in self.notes]
}
@staticmethod
def from_dict(data):
case = Case(
case_number=data["case_number"],
case_id=data["case_id"],
name=data.get("name", ""),
investigator=data.get("investigator", "")
)
case.evidence = [Evidence.from_dict(e) for e in data.get("evidence", [])]
case.notes = [Note.from_dict(n) for n in data.get("notes", [])]
return case
__all__ = ['Note', 'Evidence', 'Case', 'TagExtractor', 'IOCExtractor']