nexo-brain 2.6.13 → 2.6.14

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": "2.6.13",
3
+ "version": "2.6.14",
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
@@ -6,7 +6,7 @@
6
6
  [![GitHub stars](https://img.shields.io/github/stars/wazionapps/nexo?style=social)](https://github.com/wazionapps/nexo/stargazers)
7
7
  [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
8
8
 
9
- > Local cognitive runtime for Claude Code persistent memory, overnight learning, runtime CLI, recovery-aware background jobs, startup preflight, and doctor diagnostics. 150+ MCP tools. Benchmarked on LoCoMo (F1 0.588, +55% vs GPT-4). Claude Code plugin packaging included.
9
+ > Local cognitive runtime with a shared brain across Claude Code, Codex, Claude Desktop, and other MCP clients. Persistent memory, selectable terminal and automation backends, overnight learning, self-healing background jobs, startup preflight, and doctor diagnostics. 150+ MCP tools. Benchmarked on LoCoMo (F1 0.588, +55% vs GPT-4).
10
10
 
11
11
  **NEXO Brain transforms any MCP-compatible AI agent from a stateless assistant into a cognitive partner that remembers, learns, forgets, adapts, and builds a relationship with you over time.**
12
12
 
@@ -20,6 +20,26 @@
20
20
 
21
21
  Every time you close a session, everything is lost. Your agent doesn't remember yesterday's decisions, repeats the same mistakes, and starts from zero. NEXO Brain fixes this with a cognitive architecture modeled after how human memory actually works.
22
22
 
23
+ ## Shared Brain Across Clients
24
+
25
+ Shared brain is now the baseline:
26
+
27
+ - **Claude Code** remains the recommended path because it still has the deepest hook integration and the most battle-tested headless automation surface.
28
+ - **Codex** is supported both as an interactive terminal client and as the background automation backend.
29
+ - **Claude Desktop** can point at the same local brain through MCP.
30
+
31
+ That means NEXO now manages not only the shared runtime and MCP wiring, but also the startup layer around it:
32
+
33
+ - `nexo chat` opens the configured client instead of assuming Claude Code forever.
34
+ - Claude Code and Codex both get managed bootstrap files:
35
+ - `~/.claude/CLAUDE.md`
36
+ - `~/.codex/AGENTS.md`
37
+ - Those files now use an explicit **`CORE` / `USER`** contract, so NEXO can update product rules in `CORE` while preserving operator-specific instructions in `USER`.
38
+ - For Codex specifically, `nexo chat` and Codex headless automation inject the current bootstrap explicitly, so Codex starts as NEXO even when plain global Codex startup is inconsistent about global instructions.
39
+ - Deep Sleep now reads both Claude Code and Codex transcript stores, so overnight analysis still works even when the user spends the day in Codex.
40
+
41
+ Version `2.6.14` is the release that closes those last two parity gaps in practice: startup bootstrap parity and transcript-aware Deep Sleep parity.
42
+
23
43
  ## The Problem
24
44
 
25
45
  AI coding agents are powerful but amnesic:
@@ -198,7 +218,7 @@ This means long sessions (8+ hours) feel like one continuous conversation instea
198
218
 
199
219
  ## Cognitive Features
200
220
 
201
- NEXO Brain provides **150+ MCP tools** across 20+ categories. These features implement cognitive science concepts that go beyond basic memory:
221
+ NEXO Brain provides **150+ MCP tools** across 23 categories. These features implement cognitive science concepts that go beyond basic memory:
202
222
 
203
223
  ### Input Pipeline
204
224
 
@@ -586,6 +606,16 @@ Or use the shell alias created during install (e.g. `atlas`), which now runs `ne
586
606
 
587
607
  Your operator will greet you immediately — adapted to the time of day, resuming from where you left off. No cold starts.
588
608
 
609
+ ### Contributing
610
+
611
+ NEXO is being hardened in public, and the best contributions now are not only code changes but also real workflow feedback:
612
+
613
+ - Open issues when a client flow feels asymmetric across Claude Code, Codex, Claude Desktop, OpenClaw, or other MCP environments.
614
+ - Send PRs for docs, install UX, tests, compatibility checks, and public-facing copy.
615
+ - If you use NEXO in production-like daily work, include exact runtime symptoms and commands in bug reports. This project improves fastest when the operational reality is concrete.
616
+
617
+ The project still recommends Claude Code as the primary path, but contributions that improve Codex, client parity, installer clarity, and ecosystem integrations are especially valuable.
618
+
589
619
  ### What Gets Installed
590
620
 
591
621
  | Component | What | Where |
@@ -608,7 +638,7 @@ Your operator will greet you immediately — adapted to the time of day, resumin
608
638
  | Shared client sync | Same `nexo` MCP entry wired into Claude Code, Claude Desktop, and Codex | User config dirs |
609
639
  | Client/backend preferences | Selected interactive clients, default terminal client, automation backend, and model/reasoning profiles per client | `NEXO_HOME/config/schedule.json` |
610
640
  | Auto-diary | 3-layer system: PostToolUse every 10 calls, PreCompact emergency, heartbeat DIARY_OVERDUE | Built into hooks |
611
- | Claude Code config | MCP server + 7 hooks + 15 processes registered | ~/.claude/settings.json |
641
+ | Claude Code config | MCP server + 7 hooks + 15 managed processes registered | ~/.claude/settings.json |
612
642
 
613
643
  ### Runtime CLI
614
644
 
@@ -667,7 +697,7 @@ NEXO Brain separates **code** (immutable, in the repo or npm package) from **dat
667
697
  The plugin loader scans `src/plugins/` first (base), then `NEXO_HOME/plugins/` (personal override by filename). This dual-directory approach lets you extend NEXO without forking the repo.
668
698
  The client sync layer points Claude Code, Claude Desktop, and Codex at the same runtime and `NEXO_HOME`, so all three clients share one brain instead of drifting into separate local memories.
669
699
 
670
- ### 150+ MCP Tools across 21+ Categories
700
+ ### 150+ MCP Tools across 23 Categories
671
701
 
672
702
  | Category | Count | Tools | Purpose |
673
703
  |----------|-------|-------|---------|
package/bin/nexo-brain.js CHANGED
@@ -105,6 +105,7 @@ function getCoreRuntimeFlatFiles() {
105
105
  "client_sync.py",
106
106
  "client_preferences.py",
107
107
  "agent_runner.py",
108
+ "bootstrap_docs.py",
108
109
  "auto_update.py",
109
110
  "tools_sessions.py",
110
111
  "tools_coordination.py",
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "nexo-brain",
3
- "version": "2.6.13",
3
+ "version": "2.6.14",
4
4
  "mcpName": "io.github.wazionapps/nexo",
5
- "description": "NEXO — local cognitive runtime for Claude Code. Persistent memory, overnight learning, recovery-aware crons, personal scripts, doctor diagnostics, startup preflight, and optional power helper.",
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
+ "homepage": "https://nexo-brain.com",
6
7
  "bin": {
7
8
  "nexo-brain": "./bin/nexo-brain.js",
8
9
  "nexo": "./bin/nexo.js"
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  """Terminal client launchers and headless automation backend runner."""
4
4
 
5
+ import json
5
6
  import os
6
7
  import shutil
7
8
  import subprocess
@@ -75,6 +76,18 @@ def _headless_env(env: dict | None = None) -> dict:
75
76
  return merged
76
77
 
77
78
 
79
+ def _load_client_bootstrap_prompt(client: str) -> str:
80
+ try:
81
+ from bootstrap_docs import load_bootstrap_prompt
82
+ except Exception:
83
+ return ""
84
+ return load_bootstrap_prompt(client, nexo_home=NEXO_HOME, user_home=Path.home())
85
+
86
+
87
+ def _codex_initial_messages_config(prompt_text: str) -> str:
88
+ return f'initial_messages=[{{role="system",content={json.dumps(prompt_text, ensure_ascii=False)}}}]'
89
+
90
+
78
91
  def build_interactive_client_command(
79
92
  *,
80
93
  target: str | os.PathLike[str],
@@ -107,6 +120,9 @@ def build_interactive_client_command(
107
120
  "Codex launcher not found in PATH. Install `codex` first or reconfigure NEXO."
108
121
  )
109
122
  cmd = [codex_bin]
123
+ bootstrap_prompt = _load_client_bootstrap_prompt(CLIENT_CODEX)
124
+ if bootstrap_prompt:
125
+ cmd.extend(["-c", _codex_initial_messages_config(bootstrap_prompt)])
110
126
  if profile["model"]:
111
127
  cmd.extend(["-m", profile["model"]])
112
128
  if profile["reasoning_effort"]:
@@ -253,6 +269,9 @@ def run_automation_prompt(
253
269
  "-o",
254
270
  str(output_path),
255
271
  ]
272
+ bootstrap_prompt = _load_client_bootstrap_prompt(CLIENT_CODEX)
273
+ if bootstrap_prompt:
274
+ cmd.extend(["-c", _codex_initial_messages_config(bootstrap_prompt)])
256
275
  if resolved_model:
257
276
  cmd.extend(["-m", resolved_model])
258
277
  if resolved_effort:
@@ -735,83 +735,61 @@ def _list_section_ids(text: str) -> list[str]:
735
735
 
736
736
 
737
737
  def _migrate_claude_md() -> str | None:
738
- """Compare template version vs installed version. If newer, update core sections in user's CLAUDE.md.
739
-
740
- Returns a status message or None if no migration was needed.
741
- """
742
- template_version = _read_template_version()
743
- if not template_version:
738
+ """Sync the managed Claude bootstrap into ~/.claude/CLAUDE.md."""
739
+ try:
740
+ from bootstrap_docs import sync_client_bootstrap
741
+ except Exception as exc:
742
+ _log(f"CLAUDE.md migration import error: {exc}")
744
743
  return None
745
744
 
746
- installed_version = _read_installed_claude_md_version()
747
- if installed_version == template_version:
748
- return None # Already up to date
749
-
750
- user_md_path = _find_user_claude_md()
751
- if not user_md_path:
752
- _log("CLAUDE.md migration: no user CLAUDE.md found, skipping")
745
+ result = sync_client_bootstrap(
746
+ "claude_code",
747
+ nexo_home=NEXO_HOME,
748
+ operator_name="",
749
+ user_home=Path.home(),
750
+ )
751
+ if not result.get("ok"):
752
+ _log(f"CLAUDE.md migration failed: {result.get('error', 'unknown error')}")
753
753
  return None
754
+ version = result.get("version", "")
755
+ if version:
756
+ _write_installed_claude_md_version(version)
757
+ action = result.get("action", "updated")
758
+ if action == "unchanged":
759
+ return f"CLAUDE.md v{version}: already current"
760
+ msg = f"CLAUDE.md v{version}: {action}"
761
+ _log(msg)
762
+ return msg
754
763
 
755
- # Read both files
756
- user_md = user_md_path.read_text()
757
- template_raw = TEMPLATE_FILE.read_text()
758
- template_resolved = _resolve_placeholders(template_raw)
759
-
760
- # Get all section IDs from the template
761
- section_ids = _list_section_ids(template_resolved)
762
- if not section_ids:
763
- _log("CLAUDE.md migration: no section markers in template, skipping")
764
- _write_installed_claude_md_version(template_version)
765
- return None
766
764
 
767
- updated = user_md
768
- sections_replaced = 0
769
- sections_added = 0
765
+ def _sync_client_bootstraps(preferences: dict | None = None) -> list[str]:
766
+ try:
767
+ from bootstrap_docs import sync_enabled_bootstraps
768
+ except Exception as exc:
769
+ _log(f"Client bootstrap sync import error: {exc}")
770
+ return []
770
771
 
771
- for sid in section_ids:
772
- new_section = _extract_section(template_resolved, sid)
773
- if not new_section:
772
+ results = sync_enabled_bootstraps(
773
+ nexo_home=NEXO_HOME,
774
+ operator_name="",
775
+ user_home=Path.home(),
776
+ preferences=preferences,
777
+ )
778
+ messages: list[str] = []
779
+ for client_key, item in results.items():
780
+ if item.get("skipped"):
774
781
  continue
775
-
776
- old_section = _extract_section(updated, sid)
777
- if old_section:
778
- if old_section != new_section:
779
- updated = updated.replace(old_section, new_section)
780
- sections_replaced += 1
782
+ if not item.get("ok"):
783
+ _log(f"{client_key} bootstrap sync failed: {item.get('error', 'unknown error')}")
784
+ continue
785
+ action = item.get("action", "updated")
786
+ version = item.get("version", "")
787
+ label = "Codex AGENTS.md" if client_key == "codex" else "CLAUDE.md"
788
+ if action == "unchanged":
789
+ messages.append(f"{label} v{version}: already current")
781
790
  else:
782
- # Section doesn't exist in user's file — append before the end
783
- # (new sections added by template updates)
784
- updated = updated.rstrip() + "\n\n" + new_section + "\n"
785
- sections_added += 1
786
-
787
- # Update the version comment if present in user's file
788
- updated = re.sub(
789
- r"<!-- nexo-claude-md-version: [\d.]+ -->",
790
- f"<!-- nexo-claude-md-version: {template_version} -->",
791
- updated,
792
- )
793
- # If no version comment existed, add one at the top
794
- if "nexo-claude-md-version:" not in updated:
795
- updated = f"<!-- nexo-claude-md-version: {template_version} -->\n" + updated
796
-
797
- if sections_replaced > 0 or sections_added > 0:
798
- # Backup before writing
799
- backup_path = user_md_path.with_suffix(".md.bak")
800
- try:
801
- backup_path.write_text(user_md)
802
- except Exception:
803
- pass # Non-critical
804
-
805
- user_md_path.write_text(updated)
806
-
807
- _write_installed_claude_md_version(template_version)
808
-
809
- if sections_replaced == 0 and sections_added == 0:
810
- return f"CLAUDE.md v{template_version}: already current (version file updated)"
811
-
812
- msg = f"CLAUDE.md migrated to v{template_version}: {sections_replaced} section(s) updated, {sections_added} new section(s) added"
813
- _log(msg)
814
- return msg
791
+ messages.append(f"{label} v{version}: {action}")
792
+ return messages
815
793
 
816
794
 
817
795
  # ── Main entry point ─────────────────────────────────────────────────
@@ -824,7 +802,7 @@ def auto_update_check() -> dict:
824
802
  Phase 1 (local, safe, no network):
825
803
  - DB schema migrations
826
804
  - File-based migrations
827
- - CLAUDE.md version migration
805
+ - managed client bootstrap migration
828
806
 
829
807
  Phase 2 (network, wrapped in try/except):
830
808
  - git fetch/pull (if git repo)
@@ -835,6 +813,7 @@ def auto_update_check() -> dict:
835
813
  - git_update: str|None — git update status message
836
814
  - npm_notice: str|None — npm upgrade notice for non-git installs
837
815
  - claude_md_update: str|None — CLAUDE.md migration status
816
+ - client_bootstrap_updates: list[str] — Codex/Claude bootstrap sync statuses
838
817
  - migrations: list — file-based migration results
839
818
  - db_migrations: int — number of DB schema migrations applied
840
819
  - skipped_reason: str|None — why the network check was skipped (cooldown, etc.)
@@ -845,6 +824,7 @@ def auto_update_check() -> dict:
845
824
  "git_update": None,
846
825
  "npm_notice": None,
847
826
  "claude_md_update": None,
827
+ "client_bootstrap_updates": [],
848
828
  "migrations": [],
849
829
  "db_migrations": 0,
850
830
  "skipped_reason": None,
@@ -933,7 +913,7 @@ def auto_update_check() -> dict:
933
913
 
934
914
  # Backfill runtime CLI modules for existing installs
935
915
  try:
936
- for fname in ("cli.py", "script_registry.py", "skills_runtime.py", "cron_recovery.py", "client_preferences.py", "agent_runner.py"):
916
+ for fname in ("cli.py", "script_registry.py", "skills_runtime.py", "cron_recovery.py", "client_preferences.py", "agent_runner.py", "bootstrap_docs.py"):
937
917
  src_file = SRC_DIR / fname
938
918
  dest_file = NEXO_HOME / fname
939
919
  if src_file.is_file() and (not dest_file.exists() or src_file.stat().st_mtime > dest_file.stat().st_mtime):
@@ -1024,11 +1004,13 @@ def auto_update_check() -> dict:
1024
1004
  except Exception as e:
1025
1005
  _log(f"Template backfill error: {e}")
1026
1006
 
1027
- # CLAUDE.md version migration
1007
+ # Managed client bootstrap migration
1028
1008
  try:
1029
- result["claude_md_update"] = _migrate_claude_md()
1009
+ bootstrap_messages = _sync_client_bootstraps(schedule_data if "schedule_data" in locals() else None)
1010
+ result["client_bootstrap_updates"] = bootstrap_messages
1011
+ result["claude_md_update"] = next((msg for msg in bootstrap_messages if msg.startswith("CLAUDE.md")), None)
1030
1012
  except Exception as e:
1031
- _log(f"CLAUDE.md migration error: {e}")
1013
+ _log(f"Client bootstrap migration error: {e}")
1032
1014
 
1033
1015
  # ── Phase 2: Network operations (wrapped, never fatal) ──────────
1034
1016
  # Skip entirely if auto_update is disabled in schedule.json
@@ -1198,7 +1180,7 @@ def _backup_runtime_tree(dest: Path = NEXO_HOME) -> str:
1198
1180
  "maintenance.py", "storage_router.py", "claim_graph.py", "hnsw_index.py",
1199
1181
  "evolution_cycle.py", "migrate_embeddings.py", "auto_close_sessions.py",
1200
1182
  "client_sync.py",
1201
- "client_preferences.py", "agent_runner.py",
1183
+ "client_preferences.py", "agent_runner.py", "bootstrap_docs.py",
1202
1184
  "auto_update.py", "tools_sessions.py", "tools_coordination.py",
1203
1185
  "tools_reminders.py", "tools_reminders_crud.py", "tools_learnings.py",
1204
1186
  "tools_credentials.py", "tools_task_history.py", "tools_menu.py",
@@ -1248,7 +1230,7 @@ def _copy_runtime_from_source(src_dir: Path, repo_dir: Path, dest: Path = NEXO_H
1248
1230
  "maintenance.py", "storage_router.py", "claim_graph.py", "hnsw_index.py",
1249
1231
  "evolution_cycle.py", "migrate_embeddings.py", "auto_close_sessions.py",
1250
1232
  "client_sync.py",
1251
- "client_preferences.py", "agent_runner.py",
1233
+ "client_preferences.py", "agent_runner.py", "bootstrap_docs.py",
1252
1234
  "auto_update.py", "tools_sessions.py", "tools_coordination.py",
1253
1235
  "tools_reminders.py", "tools_reminders_crud.py", "tools_learnings.py",
1254
1236
  "tools_credentials.py", "tools_task_history.py", "tools_menu.py",
@@ -1617,6 +1599,7 @@ def startup_preflight(*, entrypoint: str, interactive: bool = False) -> dict:
1617
1599
  "git_update": None,
1618
1600
  "npm_notice": None,
1619
1601
  "claude_md_update": None,
1602
+ "client_bootstrap_updates": [],
1620
1603
  "migrations": [],
1621
1604
  "power_policy": None,
1622
1605
  "power_message": None,
@@ -1651,7 +1634,9 @@ def startup_preflight(*, entrypoint: str, interactive: bool = False) -> dict:
1651
1634
 
1652
1635
  _run_db_migrations()
1653
1636
  result["migrations"] = run_file_migrations()
1654
- result["claude_md_update"] = _migrate_claude_md()
1637
+ bootstrap_messages = _sync_client_bootstraps()
1638
+ result["client_bootstrap_updates"] = bootstrap_messages
1639
+ result["claude_md_update"] = next((msg for msg in bootstrap_messages if msg.startswith("CLAUDE.md")), None)
1655
1640
  _sync_watchdog_hash_registry()
1656
1641
  _warn_protected_runtime_location()
1657
1642
  _ensure_runtime_cli_wrapper()