feat(ncp): doctor --fix applies safe, idempotent repairs

Opt-in repair pass: reinstall Python deps on import failure, npm install when
node_modules missing, npm run build when dist missing, fetch Ollama binary.
Plain doctor stays read-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jon
2026-07-23 16:25:35 -05:00
co-authored by Claude Opus 4.8
parent 23ddb6f4fc
commit 65cf2e887e
+48 -3
View File
@@ -458,7 +458,50 @@ def cmd_logs(target) -> None:
print("Usage: ncp logs [frontend|backend|memory|all]")
def cmd_doctor() -> None:
def _apply_fixes() -> None:
"""Opt-in safe repairs (ncp doctor --fix). Only idempotent, non-destructive
actions: build the UI, install deps, fetch the Ollama binary. Anything that
could lose data or needs a decision is left to the user with a hint."""
print("\nApplying safe fixes...\n")
did = False
web, npm_path = FRONTEND_DIR, npm()
if not PYTHON.exists():
print(" ✘ Promethean venv missing — run ./install.sh to build it (skipped: heavy).")
else:
importable = subprocess.run(
[str(PYTHON), "-c", "from synapse.main import sio_app"],
cwd=str(ROOT), capture_output=True).returncode == 0
if not importable:
print(" Backend import failed — reinstalling Python dependencies...")
sys.path.insert(0, str(ROOT / "bin"))
import importlib.util
spec = importlib.util.spec_from_file_location("sync", ROOT / "bin" / "sync.py")
sync = importlib.util.module_from_spec(spec)
spec.loader.exec_module(sync)
subprocess.run([str(PYTHON), "-m", "pip", "install", "-r", sync.requirements()], cwd=str(ROOT))
did = True
if npm_path and web.is_dir() and not (web / "node_modules").is_dir():
print(" node_modules missing — running npm install...")
subprocess.run([npm_path, "install"], cwd=str(web))
did = True
if npm_path and web.is_dir() and not (web / "dist" / "index.html").exists():
print(" Built UI missing — running npm run build...")
subprocess.run([npm_path, "run", "build"], cwd=str(web))
did = True
if not ollama_bin() and os.name != "nt" and (ROOT / "bin" / "fetch-ollama.sh").exists():
print(" Ollama binary missing — fetching...")
subprocess.run(["bash", str(ROOT / "bin" / "fetch-ollama.sh")])
did = True
print("\n" + ("Fixes applied — re-run 'ncp doctor' to confirm." if did
else "Nothing to fix (all safe-repairable checks already pass)."))
def cmd_doctor(fix: bool = False) -> None:
def mark(ok, good, bad):
print(f" {'' if ok else ''} {good if ok else bad}")
@@ -536,6 +579,8 @@ def cmd_doctor() -> None:
f"Ollama models directory missing at {OLLAMA_MODELS_DIR}")
print()
cmd_status()
if fix:
_apply_fixes()
def cmd_update() -> None:
@@ -707,7 +752,7 @@ Commands:
--frontend,-f Frontend logs
--backend, -b Backend logs
doctor Run Nexus diagnostics
doctor [--fix] Run Nexus diagnostics (--fix applies safe repairs)
update Update Nexus dependencies
clean Remove runtime files and caches
@@ -753,7 +798,7 @@ def main(argv) -> int:
elif cmd == "logs":
cmd_logs(arg)
elif cmd == "doctor":
cmd_doctor()
cmd_doctor(fix="--fix" in rest)
elif cmd == "update":
cmd_update()
elif cmd == "clean":