mempalace.project_scanner
Source: mempalace/project_scanner.py
project_scanner.py — Detect projects and people from real signal.
For a codebase with build manifests or git history, this beats regex-based entity detection by a wide margin: the project's own name is already written down in package.json / pyproject.toml / Cargo.toml / go.mod / pom.xml / Gradle manifests, and the people who worked on it are in git log.
This module is used as the primary signal in mempalace init. The regex detector in entity_detector.py stays as a fallback for prose-only folders (notes, research, writing).
Public: scan(root) -> (projects, people) to_detected_dict(projects, people) -> {people: [...], projects: [...], uncertain: []}
Classes
class ProjectInfo
confidence
def confidence(self) -> floatto_signal
def to_signal(self) -> strclass PersonInfo
confidence
def confidence(self) -> floatto_signal
def to_signal(self) -> strFunctions
find_git_repos
def find_git_repos(root: Path, max_depth: int = MAX_DEPTH) -> list[Path]Return git repo roots under root (including root itself if it's a repo).
scan
def scan(root: str | os.PathLike) -> tuple[list[ProjectInfo], list[PersonInfo]]Scan root for projects and people. Returns (projects, people) sorted.
to_detected_dict
def to_detected_dict(projects: list[ProjectInfo], people: list[PersonInfo], project_cap: int = 15, people_cap: int = 15) -> dictConvert scan results into the dict shape produced by entity_detector.detect_entities.
discover_entities
def discover_entities(project_dir: str | os.PathLike, languages: tuple = ('en',), prose_file_cap: int = 10, project_cap: int = 15, people_cap: int = 15, llm_provider: object = None, show_progress: bool = True, corpus_origin: dict | None = None) -> dictTop-level entity discovery: real signals first, prose detection second.
Returns the same dict shape as entity_detector.detect_entities so it plugs into confirm_entities unchanged.
Order of signal preference:
- Package manifests (package.json, pyproject.toml, Cargo.toml, go.mod, pom.xml, Gradle manifests) → canonical project names
- Git commit authors → real people with real commit counts
- Claude Code conversation dirs (~/.claude/projects/) → per-session project names (pulled from each session's
cwdmetadata) - Regex entity detection on prose files → supplementary names only mentioned in docs/notes (not code)
- Optional LLM refinement pass — reclassifies ambiguous candidates using the caller-supplied provider
- Optional corpus-origin persona filter — when the corpus is identified as AI-dialogue, candidates whose name matches an agent_persona_name are moved to an
agent_personasbucket instead of being reported as people.
Passing llm_provider enables phase-2 refinement. The caller is responsible for constructing the provider (llm_client.get_provider) and confirming availability. Refinement is blocking-interactive: progress prints to stderr; Ctrl-C returns partial results.
Passing corpus_origin enables corpus-origin persona reclassification. The expected shape is the dict written by mempalace init to <palace>/.mempalace/origin.json (see corpus_origin.py).
