Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions mempalace/entity_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,15 @@ def _print_entity_list(entities: list, label: str):
print(f" {i + 1:2}. {e['name']:20} [{confidence_bar}] {signals_str}")


def _prompt(prompt: str, default: str = "") -> str:
"""Read user input safely; fall back to a default on EOF/non-interactive stdin."""
try:
return input(prompt).strip()
except EOFError:
print(f"\n No interactive input available — defaulting to: {default or 'accept'}")
return default


def confirm_entities(detected: dict, yes: bool = False) -> dict:
"""
Interactive confirmation step.
Expand Down Expand Up @@ -750,7 +759,7 @@ def confirm_entities(detected: dict, yes: bool = False) -> dict:
print(" [add] Add missing people or projects")
print()

choice = input(" Your choice [enter/edit/add]: ").strip().lower()
choice = _prompt(" Your choice [enter/edit/add]: ").lower()

confirmed_people = [e["name"] for e in detected["people"]]
confirmed_projects = [e["name"] for e in detected["projects"]]
Expand All @@ -760,36 +769,36 @@ def confirm_entities(detected: dict, yes: bool = False) -> dict:
if detected["uncertain"]:
print("\n Uncertain entities — classify each:")
for e in detected["uncertain"]:
ans = input(f" {e['name']} — (p)erson, (r)roject, or (s)kip? ").strip().lower()
ans = _prompt(f" {e['name']} — (p)erson, (r)roject, or (s)kip? ").lower()
if ans == "p":
confirmed_people.append(e["name"])
elif ans == "r":
confirmed_projects.append(e["name"])

# Remove wrong people
print(f"\n Current people: {', '.join(confirmed_people) or '(none)'}")
remove = input(
remove = _prompt(
" Numbers to REMOVE from people (comma-separated, or enter to skip): "
).strip()
)
if remove:
to_remove = {int(x.strip()) - 1 for x in remove.split(",") if x.strip().isdigit()}
confirmed_people = [p for i, p in enumerate(confirmed_people) if i not in to_remove]

# Remove wrong projects
print(f"\n Current projects: {', '.join(confirmed_projects) or '(none)'}")
remove = input(
remove = _prompt(
" Numbers to REMOVE from projects (comma-separated, or enter to skip): "
).strip()
)
if remove:
to_remove = {int(x.strip()) - 1 for x in remove.split(",") if x.strip().isdigit()}
confirmed_projects = [p for i, p in enumerate(confirmed_projects) if i not in to_remove]

if choice == "add" or input("\n Add any missing? [y/N]: ").strip().lower() == "y":
if choice == "add" or _prompt("\n Add any missing? [y/N]: ").lower() == "y":
while True:
name = input(" Name (or enter to stop): ").strip()
name = _prompt(" Name (or enter to stop): ")
if not name:
break
kind = input(f" Is '{name}' a (p)erson or (r)roject? ").strip().lower()
kind = _prompt(f" Is '{name}' a (p)erson or (r)roject? ").lower()
if kind == "p":
confirmed_people.append(name)
elif kind == "r":
Expand Down
21 changes: 14 additions & 7 deletions mempalace/room_detector_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ def print_proposed_structure(project_name: str, rooms: list, total_files: int, s
print(f"\n{'─' * 55}")


def _prompt(prompt: str, default: str = "") -> str:
"""Read user input safely; fall back to a default on EOF/non-interactive stdin."""
try:
return input(prompt).strip()
except EOFError:
print(f"\n No interactive input available — defaulting to: {default or 'accept'}")
return default


def get_user_approval(rooms: list) -> list:
"""Same approval flow as AI version."""
print(" Review the proposed rooms above.")
Expand All @@ -224,7 +233,7 @@ def get_user_approval(rooms: list) -> list:
print(" [add] Add a room manually")
print()

choice = input(" Your choice [enter/edit/add]: ").strip().lower()
choice = _prompt(" Your choice [enter/edit/add]: ").lower()

if choice in ("", "y", "yes"):
return rooms
Expand All @@ -233,19 +242,17 @@ def get_user_approval(rooms: list) -> list:
print("\n Current rooms:")
for i, room in enumerate(rooms):
print(f" {i + 1}. {room['name']} — {room['description']}")
remove = input("\n Room numbers to REMOVE (comma-separated, or enter to skip): ").strip()
remove = _prompt("\n Room numbers to REMOVE (comma-separated, or enter to skip): ")
if remove:
to_remove = {int(x.strip()) - 1 for x in remove.split(",") if x.strip().isdigit()}
rooms = [r for i, r in enumerate(rooms) if i not in to_remove]

if choice == "add" or input("\n Add any missing rooms? [y/N]: ").strip().lower() == "y":
if choice == "add" or _prompt("\n Add any missing rooms? [y/N]: ").lower() == "y":
while True:
new_name = (
input(" New room name (or enter to stop): ").strip().lower().replace(" ", "_")
)
new_name = _prompt(" New room name (or enter to stop): ").lower().replace(" ", "_")
if not new_name:
break
new_desc = input(f" Description for '{new_name}': ").strip()
new_desc = _prompt(f" Description for '{new_name}': ")
rooms.append({"name": new_name, "description": new_desc, "keywords": [new_name]})
print(f" Added: {new_name}")

Expand Down
30 changes: 30 additions & 0 deletions tests/test_noninteractive_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import builtins

from mempalace.entity_detector import confirm_entities
from mempalace.room_detector_local import get_user_approval


def test_room_approval_defaults_to_accept_on_eof(monkeypatch):
rooms = [{"name": "src", "description": "Files from src/", "keywords": ["src"]}]

def raise_eof(_prompt):
raise EOFError

monkeypatch.setattr(builtins, "input", raise_eof)
assert get_user_approval(rooms) == rooms


def test_entity_confirmation_defaults_to_accept_on_eof(monkeypatch):
detected = {
"people": [{"name": "Alice", "confidence": 1.0, "source": "test", "signals": []}],
"projects": [{"name": "MemPalace", "confidence": 1.0, "source": "test", "signals": []}],
"uncertain": [],
}

def raise_eof(_prompt):
raise EOFError

monkeypatch.setattr(builtins, "input", raise_eof)
confirmed = confirm_entities(detected)

assert confirmed == {"people": ["Alice"], "projects": ["MemPalace"]}