nexo-brain 5.3.3 → 5.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "5.3.3",
3
+ "version": "5.3.4",
4
4
  "description": "Local cognitive runtime for Claude Code \u2014 persistent memory, overnight learning, doctor diagnostics, personal scripts, recovery-aware jobs, startup preflight, and optional dashboard/power helper.",
5
5
  "author": {
6
6
  "name": "NEXO Brain",
package/README.md CHANGED
@@ -18,6 +18,8 @@
18
18
 
19
19
  [Watch the overview video](https://nexo-brain.com/watch/) · [Watch on YouTube](https://www.youtube.com/watch?v=i2lkGhKyVqI) · [Open the infographic](https://nexo-brain.com/assets/nexo-brain-infographic-v5.png)
20
20
 
21
+ Version `5.3.4` closes the last packaged-runtime core/personal leak from the hook migration work: legacy hook aliases stay out of the personal bucket, `nexo update` removes those retired aliases when the canonical hooks already exist, and both `nexo` and `nexo chat` now show latest vs installed version at a glance.
22
+
21
23
  Start here:
22
24
  - [5-minute quickstart](docs/quickstart-5-minutes.md)
23
25
  - [Workflow quickstart](docs/workflows-quickstart.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "5.3.3",
3
+ "version": "5.3.4",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
5
  "description": "NEXO Brain — Shared brain for AI agents. Persistent memory, semantic RAG, natural forgetting, metacognitive guard, trust scoring, 150+ MCP tools. Works with Claude Code, Codex, Claude Desktop & any MCP client. 100% local, free.",
6
6
  "homepage": "https://nexo-brain.com",
@@ -364,6 +364,12 @@ def _cleanup_retired_runtime_files():
364
364
  NEXO_HOME / "scripts" / "heartbeat-user-msg.sh",
365
365
  NEXO_HOME / "hooks" / "heartbeat-guard.sh",
366
366
  ]
367
+ conditional_retired = [
368
+ (NEXO_HOME / "scripts" / "nexo-postcompact.sh", NEXO_HOME / "hooks" / "post-compact.sh"),
369
+ (NEXO_HOME / "scripts" / "nexo-memory-precompact.sh", NEXO_HOME / "hooks" / "pre-compact.sh"),
370
+ (NEXO_HOME / "scripts" / "nexo-memory-stop.sh", NEXO_HOME / "hooks" / "session-stop.sh"),
371
+ (NEXO_HOME / "scripts" / "nexo-session-briefing.sh", NEXO_HOME / "hooks" / "session-start.sh"),
372
+ ]
367
373
  for target in retired:
368
374
  try:
369
375
  if target.exists():
@@ -375,6 +381,13 @@ def _cleanup_retired_runtime_files():
375
381
  _log(f"Removed retired runtime file: {target.name}")
376
382
  except Exception as e:
377
383
  _log(f"Retired runtime cleanup warning ({target.name}): {e}")
384
+ for target, canonical in conditional_retired:
385
+ try:
386
+ if target.exists() and canonical.exists():
387
+ target.unlink()
388
+ _log(f"Removed retired runtime alias: {target.name} (canonical: {canonical.name})")
389
+ except Exception as e:
390
+ _log(f"Retired runtime alias cleanup warning ({target.name}): {e}")
378
391
 
379
392
 
380
393
  def _sync_crons():
package/src/cli.py CHANGED
@@ -38,6 +38,7 @@ import os
38
38
  import shutil
39
39
  import subprocess
40
40
  import sys
41
+ import time
41
42
  from pathlib import Path
42
43
 
43
44
  from runtime_home import export_resolved_nexo_home
@@ -49,6 +50,8 @@ TERMINAL_CLIENT_LABELS = {
49
50
  "codex": "Codex",
50
51
  }
51
52
  TERMINAL_CLIENT_ORDER = ("claude_code", "codex")
53
+ VERSION_STATUS_CACHE = NEXO_HOME / "config" / "cli-version-status.json"
54
+ LATEST_NPM_PACKAGE = "nexo-brain"
52
55
 
53
56
 
54
57
  def _get_version() -> str:
@@ -67,6 +70,67 @@ def _get_version() -> str:
67
70
  continue
68
71
  return "?"
69
72
 
73
+
74
+ def _load_latest_version_cache(max_age_seconds: int = 6 * 3600) -> str | None:
75
+ try:
76
+ payload = json.loads(VERSION_STATUS_CACHE.read_text())
77
+ except Exception:
78
+ return None
79
+ version = str(payload.get("latest", "")).strip()
80
+ checked_at = float(payload.get("checked_at", 0) or 0)
81
+ if not version:
82
+ return None
83
+ if checked_at and (time.time() - checked_at) > max_age_seconds:
84
+ return None
85
+ return version
86
+
87
+
88
+ def _save_latest_version_cache(version: str) -> None:
89
+ VERSION_STATUS_CACHE.parent.mkdir(parents=True, exist_ok=True)
90
+ VERSION_STATUS_CACHE.write_text(json.dumps({
91
+ "latest": version,
92
+ "checked_at": time.time(),
93
+ }))
94
+
95
+
96
+ def _fetch_latest_version(timeout_seconds: int = 2) -> str | None:
97
+ try:
98
+ result = subprocess.run(
99
+ ["npm", "view", LATEST_NPM_PACKAGE, "version"],
100
+ capture_output=True,
101
+ text=True,
102
+ timeout=timeout_seconds,
103
+ )
104
+ except Exception:
105
+ return None
106
+ if result.returncode != 0:
107
+ return None
108
+ latest = result.stdout.strip()
109
+ if not latest:
110
+ return None
111
+ try:
112
+ _save_latest_version_cache(latest)
113
+ except Exception:
114
+ pass
115
+ return latest
116
+
117
+
118
+ def _should_refresh_latest_version() -> bool:
119
+ try:
120
+ return sys.stdout.isatty() or sys.stderr.isatty()
121
+ except Exception:
122
+ return False
123
+
124
+
125
+ def _version_status_line() -> str:
126
+ installed = _get_version()
127
+ latest = _load_latest_version_cache()
128
+ if latest is None and _should_refresh_latest_version():
129
+ latest = _fetch_latest_version()
130
+ if latest:
131
+ return f"NEXO Latest: v{latest} | Installed: v{installed}"
132
+ return f"NEXO Installed: v{installed}"
133
+
70
134
  # Ensure src/ is on path for imports
71
135
  if str(NEXO_CODE) not in sys.path:
72
136
  sys.path.insert(0, str(NEXO_CODE))
@@ -1011,6 +1075,7 @@ def _prompt_for_terminal_client(
1011
1075
  def _chat(args):
1012
1076
  target = args.path or "."
1013
1077
  selected_client = getattr(args, "client", None)
1078
+ print(f"[NEXO] {_version_status_line()}", file=sys.stderr)
1014
1079
 
1015
1080
  try:
1016
1081
  from auto_update import startup_preflight
@@ -1499,6 +1564,7 @@ def _uninstall(args):
1499
1564
  def _print_help():
1500
1565
  v = _get_version()
1501
1566
  print(f"""NEXO Runtime CLI v{v}
1567
+ {_version_status_line()}
1502
1568
 
1503
1569
  Commands:
1504
1570
  nexo chat [path] [--client claude_code|codex] Launch a NEXO terminal client
@@ -50,6 +50,10 @@ _LEGACY_CORE_RUNTIME_FILES = {
50
50
  "heartbeat-enforcement.py",
51
51
  "heartbeat-posttool.sh",
52
52
  "heartbeat-user-msg.sh",
53
+ "nexo-memory-precompact.sh",
54
+ "nexo-memory-stop.sh",
55
+ "nexo-postcompact.sh",
56
+ "nexo-session-briefing.sh",
53
57
  }
54
58
 
55
59
  # Forbidden patterns — direct DB access from personal scripts