nexo-brain 5.3.8 → 5.3.9
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/doctor/providers/runtime.py +7 -0
- package/src/plugins/update.py +17 -5
- package/src/script_registry.py +42 -12
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.9",
|
|
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,7 +18,7 @@
|
|
|
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.
|
|
21
|
+
Version `5.3.9` is the current packaged-runtime line: packaged updates now rebuild the core artifact manifest from the canonical npm package source instead of the live `~/.nexo/scripts` directory, so personal scripts stop being reclassified as core, portable user-data export keeps including them, and runtime doctor can recover personal LaunchAgent ownership cleanly after a bad `5.3.8` update.
|
|
22
22
|
|
|
23
23
|
Start here:
|
|
24
24
|
- [5-minute quickstart](docs/quickstart-5-minutes.md)
|
|
@@ -89,7 +89,7 @@ Versions `3.1.7` through `3.2.0` close the recent-memory gap:
|
|
|
89
89
|
- when even that misses, NEXO now exposes raw transcript fallback tools for Claude Code and Codex session stores
|
|
90
90
|
- NEXO can now inspect itself through a live system catalog derived from canonical sources instead of relying only on stale docs or operator memory
|
|
91
91
|
|
|
92
|
-
Version `5.3.
|
|
92
|
+
Version `5.3.9` is the packaged core-artifact manifest heal for `5.3.8`: packaged updates now rebuild `runtime-core-artifacts.json` from the canonical npm package `src/` tree instead of scanning the live `~/.nexo/scripts` directory, script classification prefers that canonical packaged source when available, and runtime doctor syncs personal scripts before LaunchAgent inventory so personal automations recover cleanly instead of being mistaken for unknown core drift. Version `5.3.8` was the immediate packaged-migration hotfix for `5.3.7`: the installer/runtime migrator now discovers all top-level runtime Python modules from `src/` dynamically instead of relying on a manual allowlist, so new product surfaces like `nexo export` / `nexo import` actually arrive in `~/.nexo` after update instead of being present only in the published npm tarball. Version `5.3.7` closed the remaining packaged-runtime happy-path gap and finally exposed portable user-data migration commands: packaged `nexo update` now self-heals cron definitions and LaunchAgents after a successful npm bump, new `nexo export` / `nexo import` commands move operator data as a safe bundle instead of leaving that flow implicit, and runtime doctor now distinguishes tracked historical Codex drift from an actually broken runtime so cleaned installs stop staying red for stale transcript debt alone. Version `5.3.6` hardened the Claude Code bootstrap path and related runtime hygiene: managed client sync now writes the NEXO MCP server where current Claude Code actually reads it (`~/.claude.json`), script classification is stricter about core-vs-personal runtime artifacts, schedule status distinguishes genuinely running jobs from broken ones, and retroactive learnings stop opening keyword-only false positives outside their declared `applies_to` scope. Version `5.3.5` already keeps CLI version visibility honest right after `nexo update`: if the cached npm version lags behind the runtime you just installed, `nexo` / `nexo chat` now clamp `Latest` to the installed version and refresh the cache instead of showing a stale older release. Version `5.3.4` already cleaned up legacy core alias leakage and added the version-status banner. Version `5.3.3` closed the remaining packaged-runtime doctor mismatch: the built-in hourly backup helper is now inventoried as a core LaunchAgent, so clean installs no longer get a false unknown-LaunchAgent warning. Version `5.3.2` already hardened the runtime boundary by persisting which runtime scripts/hooks are core product artifacts, keeping `nexo scripts` from mixing those into the personal bucket, and migrating the legacy Claude Code heartbeat wrappers into managed core hooks.
|
|
93
93
|
|
|
94
94
|
Version `5.3.1` normalizes packaged npm installs so they behave like packaged npm installs: `nexo update` now keeps the runtime anchored to `~/.nexo`, refreshes packaged bootstrap/client artifacts after upgrade, avoids repo-only release-artifact drift in installed runtimes, and keeps personal scripts on the canonical packaged path.
|
|
95
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexo-brain",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.9",
|
|
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",
|
|
@@ -1621,6 +1621,13 @@ def check_launchagent_inventory() -> DoctorCheck:
|
|
|
1621
1621
|
summary="No com.nexo LaunchAgents discovered on this Mac",
|
|
1622
1622
|
)
|
|
1623
1623
|
|
|
1624
|
+
try:
|
|
1625
|
+
from script_registry import sync_personal_scripts
|
|
1626
|
+
|
|
1627
|
+
sync_personal_scripts(prune_missing=True)
|
|
1628
|
+
except Exception:
|
|
1629
|
+
pass
|
|
1630
|
+
|
|
1624
1631
|
known_ids = _known_nexo_launchagent_ids()
|
|
1625
1632
|
unknown_ids = sorted(actual_ids - known_ids)
|
|
1626
1633
|
if not unknown_ids:
|
package/src/plugins/update.py
CHANGED
|
@@ -75,6 +75,14 @@ def _find_npm_pkg_src() -> Path | None:
|
|
|
75
75
|
pass
|
|
76
76
|
return None
|
|
77
77
|
|
|
78
|
+
|
|
79
|
+
def _core_artifact_source_dir() -> Path | None:
|
|
80
|
+
"""Return the canonical source directory for packaged core artifacts."""
|
|
81
|
+
if _PACKAGED_INSTALL:
|
|
82
|
+
return _find_npm_pkg_src()
|
|
83
|
+
return SRC_DIR
|
|
84
|
+
|
|
85
|
+
|
|
78
86
|
def _is_git_repo() -> bool:
|
|
79
87
|
"""Check if REPO_DIR is a valid git repository."""
|
|
80
88
|
return (REPO_DIR / ".git").exists() or (REPO_DIR / ".git").is_file()
|
|
@@ -83,7 +91,11 @@ def _is_git_repo() -> bool:
|
|
|
83
91
|
def _refresh_installed_manifest():
|
|
84
92
|
"""Refresh packaged crons and persist the runtime core-artifacts manifest."""
|
|
85
93
|
try:
|
|
86
|
-
|
|
94
|
+
artifact_src = _core_artifact_source_dir()
|
|
95
|
+
if artifact_src is None:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
src_crons = artifact_src / "crons"
|
|
87
99
|
dst_crons = NEXO_HOME / "crons"
|
|
88
100
|
if src_crons.exists():
|
|
89
101
|
dst_crons.mkdir(parents=True, exist_ok=True)
|
|
@@ -98,13 +110,13 @@ def _refresh_installed_manifest():
|
|
|
98
110
|
payload = {
|
|
99
111
|
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
100
112
|
"script_names": sorted(
|
|
101
|
-
f.name for f in (
|
|
113
|
+
f.name for f in (artifact_src / "scripts").iterdir()
|
|
102
114
|
if f.is_file()
|
|
103
|
-
) if (
|
|
115
|
+
) if (artifact_src / "scripts").is_dir() else [],
|
|
104
116
|
"hook_names": sorted(
|
|
105
|
-
f.name for f in (
|
|
117
|
+
f.name for f in (artifact_src / "hooks").iterdir()
|
|
106
118
|
if f.is_file()
|
|
107
|
-
) if (
|
|
119
|
+
) if (artifact_src / "hooks").is_dir() else [],
|
|
108
120
|
}
|
|
109
121
|
(config_dir / "runtime-core-artifacts.json").write_text(
|
|
110
122
|
json.dumps(payload, indent=2, ensure_ascii=False) + "\n"
|
package/src/script_registry.py
CHANGED
|
@@ -161,10 +161,36 @@ def _add_filenames_from_dir(names: set[str], directory: Path, *, skip_if_scripts
|
|
|
161
161
|
names.add(item.name)
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
def _find_packaged_core_source_dir() -> Path | None:
|
|
165
|
+
repo_root = NEXO_CODE.parent
|
|
166
|
+
if (repo_root / ".git").exists() or (repo_root / ".git").is_file():
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
with contextlib.suppress(Exception):
|
|
170
|
+
result = subprocess.run(
|
|
171
|
+
["npm", "root", "-g"],
|
|
172
|
+
capture_output=True,
|
|
173
|
+
text=True,
|
|
174
|
+
timeout=10,
|
|
175
|
+
)
|
|
176
|
+
if result.returncode == 0:
|
|
177
|
+
candidate = Path(result.stdout.strip()) / "nexo-brain" / "src"
|
|
178
|
+
if candidate.is_dir():
|
|
179
|
+
return candidate
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
|
|
164
183
|
def load_core_script_names() -> set[str]:
|
|
165
184
|
"""Load every core-managed runtime artifact name that must never be treated as personal."""
|
|
166
185
|
names: set[str] = set()
|
|
167
|
-
|
|
186
|
+
packaged_src = _find_packaged_core_source_dir()
|
|
187
|
+
|
|
188
|
+
manifest_candidates = []
|
|
189
|
+
if packaged_src is not None:
|
|
190
|
+
manifest_candidates.append(packaged_src / "crons" / "manifest.json")
|
|
191
|
+
manifest_candidates.extend([NEXO_CODE / "crons" / "manifest.json", NEXO_HOME / "crons" / "manifest.json"])
|
|
192
|
+
|
|
193
|
+
for manifest_path in manifest_candidates:
|
|
168
194
|
if manifest_path.exists():
|
|
169
195
|
try:
|
|
170
196
|
data = json.loads(manifest_path.read_text())
|
|
@@ -176,17 +202,21 @@ def load_core_script_names() -> set[str]:
|
|
|
176
202
|
except Exception:
|
|
177
203
|
continue
|
|
178
204
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
if packaged_src is not None:
|
|
206
|
+
_add_filenames_from_dir(names, packaged_src / "hooks")
|
|
207
|
+
_add_filenames_from_dir(names, packaged_src / "scripts")
|
|
208
|
+
else:
|
|
209
|
+
for artifact_path in (
|
|
210
|
+
NEXO_HOME / "config" / "runtime-core-artifacts.json",
|
|
211
|
+
NEXO_CODE / "config" / "runtime-core-artifacts.json",
|
|
212
|
+
NEXO_CODE.parent / "config" / "runtime-core-artifacts.json",
|
|
213
|
+
):
|
|
214
|
+
if artifact_path.exists():
|
|
215
|
+
_add_runtime_artifact_names(names, artifact_path)
|
|
216
|
+
|
|
217
|
+
_add_filenames_from_dir(names, NEXO_HOME / "hooks")
|
|
218
|
+
_add_filenames_from_dir(names, NEXO_CODE / "hooks")
|
|
219
|
+
_add_filenames_from_dir(names, NEXO_CODE / "scripts", skip_if_scripts_dir=True)
|
|
190
220
|
|
|
191
221
|
for legacy_name, canonical_name in _LEGACY_CORE_SCRIPT_ALIASES.items():
|
|
192
222
|
if canonical_name in names:
|