nexo-brain 1.7.0 → 2.1.0
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/README.md +160 -60
- package/bin/nexo-brain.js +680 -381
- package/package.json +18 -3
- package/scripts/migrate-to-unified.sh +813 -0
- package/scripts/migrate-v1.7-to-v1.8.py +214 -0
- package/scripts/pre-commit-check.sh +1 -1
- package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
- package/src/auto_close_sessions.py +1 -1
- package/src/auto_update.py +634 -0
- package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
- package/src/cognitive/_core.py +7 -3
- package/src/cognitive/_decay.py +1 -1
- package/src/cognitive/_search.py +1 -0
- package/src/cognitive/_trust.py +3 -3
- package/src/crons/manifest.json +106 -0
- package/src/crons/sync.py +217 -0
- package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
- package/src/dashboard/app.py +24 -4
- package/src/dashboard/templates/dashboard.html +3 -2
- package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
- package/src/db/_core.py +5 -1
- package/src/db/_episodic.py +2 -4
- package/src/db/_reminders.py +45 -6
- package/src/db/_schema.py +31 -0
- package/src/evolution_cycle.py +33 -11
- package/src/hooks/auto_capture.py +1 -1
- package/src/hooks/capture-tool-logs.sh +76 -0
- package/src/hooks/inbox-hook.sh +2 -1
- package/src/hooks/post-compact.sh +2 -1
- package/src/hooks/pre-compact.sh +104 -2
- package/src/hooks/session-start.sh +6 -2
- package/src/hooks/session-stop.sh +4 -2
- package/src/kg_populate.py +4 -1
- package/src/migrate_embeddings.py +4 -1
- package/src/plugin_loader.py +100 -34
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/agents.py +2 -2
- package/src/plugins/backup.py +5 -4
- package/src/plugins/core_rules.py +37 -16
- package/src/plugins/episodic_memory.py +14 -5
- package/src/plugins/evolution.py +6 -2
- package/src/plugins/guard.py +20 -11
- package/src/plugins/update.py +256 -0
- package/src/requirements.txt +12 -0
- package/src/scripts/check-context.py +8 -3
- package/src/scripts/deep-sleep/__pycache__/extract.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/apply_findings.py +514 -169
- package/src/scripts/deep-sleep/collect.py +480 -0
- package/src/scripts/deep-sleep/extract-prompt.md +233 -0
- package/src/scripts/deep-sleep/extract.py +249 -0
- package/src/scripts/deep-sleep/synthesize-prompt.md +168 -0
- package/src/scripts/deep-sleep/synthesize.py +191 -0
- package/src/scripts/nexo-auto-update.py +4 -211
- package/src/scripts/nexo-backup.sh +5 -13
- package/src/scripts/nexo-brain-activation.sh +26 -26
- package/src/scripts/nexo-catchup.py +36 -25
- package/src/scripts/nexo-cognitive-decay.py +7 -3
- package/src/scripts/nexo-daily-self-audit.py +44 -15
- package/src/scripts/nexo-deep-sleep.sh +31 -16
- package/src/scripts/nexo-evolution-run.py +21 -11
- package/src/scripts/nexo-followup-hygiene.py +6 -4
- package/src/scripts/nexo-github-monitor.py +12 -8
- package/src/scripts/nexo-immune.py +6 -4
- package/src/scripts/nexo-inbox-hook.sh +2 -1
- package/src/scripts/nexo-install.py +4 -225
- package/src/scripts/nexo-learning-housekeep.py +7 -3
- package/src/scripts/nexo-learning-validator.py +1 -22
- package/src/scripts/nexo-migrate.py +9 -3
- package/src/scripts/nexo-postmortem-consolidator.py +17 -10
- package/src/scripts/nexo-pre-commit.py +3 -1
- package/src/scripts/nexo-prevent-sleep.sh +29 -0
- package/src/scripts/nexo-proactive-dashboard.py +5 -4
- package/src/scripts/nexo-runtime-preflight.py +59 -55
- package/src/scripts/nexo-send-email.py +1 -1
- package/src/scripts/nexo-send-reply.py +3 -1
- package/src/scripts/nexo-sleep.py +13 -9
- package/src/scripts/nexo-snapshot-restore.sh +2 -1
- package/src/scripts/nexo-synthesis.py +11 -7
- package/src/scripts/nexo-tcc-approve.sh +79 -0
- package/src/scripts/nexo-update.sh +161 -0
- package/src/scripts/nexo-watchdog-smoke.py +18 -13
- package/src/scripts/nexo-watchdog.sh +22 -13
- package/src/server.py +77 -28
- package/src/storage_router.py +6 -2
- package/src/tools_learnings.py +6 -6
- package/src/tools_menu.py +1 -1
- package/src/tools_reminders_crud.py +10 -8
- package/src/tools_sessions.py +76 -4
- package/templates/CLAUDE.md.template +14 -80
- package/templates/launchagents/README.md +7 -7
- package/templates/launchagents/com.nexo.auto-close-sessions.plist +5 -1
- package/templates/launchagents/com.nexo.catchup.plist +4 -0
- package/templates/launchagents/com.nexo.cognitive-decay.plist +7 -0
- package/templates/launchagents/com.nexo.dashboard.plist +5 -1
- package/templates/launchagents/com.nexo.deep-sleep.plist +4 -0
- package/templates/launchagents/com.nexo.evolution.plist +4 -0
- package/templates/launchagents/com.nexo.followup-hygiene.plist +4 -0
- package/templates/launchagents/com.nexo.github-monitor.plist +3 -1
- package/templates/launchagents/com.nexo.immune.plist +4 -0
- package/templates/launchagents/com.nexo.postmortem.plist +4 -0
- package/templates/launchagents/com.nexo.self-audit.plist +4 -0
- package/templates/launchagents/com.nexo.synthesis.plist +4 -0
- package/templates/launchagents/com.nexo.watchdog.plist +4 -0
- package/templates/openclaw.json +1 -1
- package/tests/conftest.py +2 -2
- package/tests/test_cognitive.py +7 -6
- package/tests/test_migrations.py +26 -0
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
- package/src/__pycache__/server.cpython-314.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
- package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/analyze_session.py +0 -217
- package/src/scripts/deep-sleep/collect_transcripts.py +0 -145
- package/src/scripts/deep-sleep/prompt.md +0 -109
- package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-314-pytest-9.0.2.pyc +0 -0
|
@@ -1,227 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
Creates ~/.nexo/ directory structure, initializes databases,
|
|
5
|
-
copies scripts/hooks/plugins from the repo, and sets NEXO_HOME
|
|
6
|
-
in the user's shell profile.
|
|
7
|
-
|
|
8
|
-
Usage:
|
|
9
|
-
python3 nexo-install.py # interactive install
|
|
10
|
-
python3 nexo-install.py --yes # skip confirmations
|
|
11
|
-
python3 nexo-install.py --nexo-home /path # custom location
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
import argparse
|
|
15
|
-
import json
|
|
16
|
-
import os
|
|
17
|
-
import shutil
|
|
18
|
-
import subprocess
|
|
2
|
+
"""DEPRECATED: Use 'npx nexo-brain' instead. This installer is no longer maintained."""
|
|
19
3
|
import sys
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
REPO_ROOT = Path(__file__).resolve().parent.parent.parent # nexo/src/scripts -> nexo/
|
|
24
|
-
|
|
25
|
-
# Required directory structure
|
|
26
|
-
NEXO_DIRS = [
|
|
27
|
-
"db",
|
|
28
|
-
"brain",
|
|
29
|
-
"logs",
|
|
30
|
-
"operations",
|
|
31
|
-
"coordination",
|
|
32
|
-
"scripts",
|
|
33
|
-
"hooks",
|
|
34
|
-
"plugins",
|
|
35
|
-
"backups",
|
|
36
|
-
"memory",
|
|
37
|
-
"docs",
|
|
38
|
-
"projects",
|
|
39
|
-
"learnings",
|
|
40
|
-
"agents",
|
|
41
|
-
"skills",
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def get_version() -> str:
|
|
46
|
-
"""Read version from package.json."""
|
|
47
|
-
pkg = REPO_ROOT / "package.json"
|
|
48
|
-
if not pkg.exists():
|
|
49
|
-
return "0.0.0"
|
|
50
|
-
return json.loads(pkg.read_text()).get("version", "0.0.0")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def detect_shell() -> tuple[str, Path]:
|
|
54
|
-
"""Detect user's shell and return (shell_name, profile_path)."""
|
|
55
|
-
shell = os.environ.get("SHELL", "/bin/bash")
|
|
56
|
-
home = Path.home()
|
|
57
|
-
|
|
58
|
-
if "zsh" in shell:
|
|
59
|
-
return "zsh", home / ".zshrc"
|
|
60
|
-
elif "fish" in shell:
|
|
61
|
-
return "fish", home / ".config" / "fish" / "config.fish"
|
|
62
|
-
else:
|
|
63
|
-
# bash — prefer .bashrc, fall back to .bash_profile
|
|
64
|
-
bashrc = home / ".bashrc"
|
|
65
|
-
if bashrc.exists():
|
|
66
|
-
return "bash", bashrc
|
|
67
|
-
return "bash", home / ".bash_profile"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def create_directory_structure(nexo_home: Path):
|
|
71
|
-
"""Create all required directories."""
|
|
72
|
-
for d in NEXO_DIRS:
|
|
73
|
-
(nexo_home / d).mkdir(parents=True, exist_ok=True)
|
|
74
|
-
print(f" Created {len(NEXO_DIRS)} directories in {nexo_home}")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def initialize_databases(nexo_home: Path):
|
|
78
|
-
"""Initialize empty databases with schema."""
|
|
79
|
-
os.environ["NEXO_HOME"] = str(nexo_home)
|
|
80
|
-
os.environ["NEXO_SKIP_FS_INDEX"] = "1"
|
|
81
|
-
|
|
82
|
-
src_dir = REPO_ROOT / "src"
|
|
83
|
-
if str(src_dir) not in sys.path:
|
|
84
|
-
sys.path.insert(0, str(src_dir))
|
|
85
|
-
|
|
86
|
-
try:
|
|
87
|
-
from db import init_db
|
|
88
|
-
init_db()
|
|
89
|
-
print(" Initialized nexo.db with schema.")
|
|
90
|
-
except Exception as e:
|
|
91
|
-
print(f" WARNING: DB init error: {e}", file=sys.stderr)
|
|
92
|
-
print(" You can run schema migrations later with nexo-migrate.py")
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def copy_repo_files(nexo_home: Path):
|
|
96
|
-
"""Copy hooks, scripts, and plugins from the repo."""
|
|
97
|
-
src = REPO_ROOT / "src"
|
|
98
|
-
|
|
99
|
-
# Copy scripts
|
|
100
|
-
scripts_src = src / "scripts"
|
|
101
|
-
scripts_dst = nexo_home / "scripts"
|
|
102
|
-
if scripts_src.exists():
|
|
103
|
-
count = 0
|
|
104
|
-
for f in scripts_src.iterdir():
|
|
105
|
-
if f.is_file() and not f.name.startswith("__"):
|
|
106
|
-
shutil.copy2(f, scripts_dst / f.name)
|
|
107
|
-
count += 1
|
|
108
|
-
print(f" Copied {count} scripts.")
|
|
109
|
-
|
|
110
|
-
# Copy plugins
|
|
111
|
-
plugins_src = src / "plugins"
|
|
112
|
-
plugins_dst = nexo_home / "plugins"
|
|
113
|
-
if plugins_src.exists():
|
|
114
|
-
count = 0
|
|
115
|
-
for f in plugins_src.iterdir():
|
|
116
|
-
if f.is_file() and f.suffix == ".py" and not f.name.startswith("__"):
|
|
117
|
-
shutil.copy2(f, plugins_dst / f.name)
|
|
118
|
-
count += 1
|
|
119
|
-
print(f" Copied {count} plugins.")
|
|
120
|
-
|
|
121
|
-
# Copy hooks (templates directory)
|
|
122
|
-
hooks_src = REPO_ROOT / "templates" / "hooks"
|
|
123
|
-
hooks_dst = nexo_home / "hooks"
|
|
124
|
-
if hooks_src.exists():
|
|
125
|
-
count = 0
|
|
126
|
-
for f in hooks_src.iterdir():
|
|
127
|
-
if f.is_file():
|
|
128
|
-
shutil.copy2(f, hooks_dst / f.name)
|
|
129
|
-
count += 1
|
|
130
|
-
if count:
|
|
131
|
-
print(f" Copied {count} hooks.")
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def set_nexo_home_env(nexo_home: Path, shell_name: str, profile_path: Path):
|
|
135
|
-
"""Add NEXO_HOME export to shell profile if not already present."""
|
|
136
|
-
export_line = f'export NEXO_HOME="{nexo_home}"'
|
|
137
|
-
|
|
138
|
-
if profile_path.exists():
|
|
139
|
-
content = profile_path.read_text()
|
|
140
|
-
if "NEXO_HOME" in content:
|
|
141
|
-
print(f" NEXO_HOME already set in {profile_path}")
|
|
142
|
-
return
|
|
143
|
-
else:
|
|
144
|
-
content = ""
|
|
145
|
-
|
|
146
|
-
with open(profile_path, "a") as f:
|
|
147
|
-
f.write(f"\n# NEXO Brain — cognitive co-operator\n")
|
|
148
|
-
f.write(f"{export_line}\n")
|
|
149
|
-
|
|
150
|
-
print(f" Added NEXO_HOME to {profile_path}")
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def write_version_json(nexo_home: Path, version: str):
|
|
154
|
-
"""Write version.json."""
|
|
155
|
-
data = {
|
|
156
|
-
"version": version,
|
|
157
|
-
"installed_at": datetime.now().isoformat(timespec="seconds"),
|
|
158
|
-
"nexo_home": str(nexo_home),
|
|
159
|
-
}
|
|
160
|
-
(nexo_home / "version.json").write_text(json.dumps(data, indent=2) + "\n")
|
|
161
|
-
print(f" Written version.json (v{version})")
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def main():
|
|
165
|
-
parser = argparse.ArgumentParser(description="NEXO Brain — First-time setup")
|
|
166
|
-
parser.add_argument("--yes", "-y", action="store_true", help="Skip confirmations")
|
|
167
|
-
parser.add_argument("--nexo-home", type=str, default=None, help="Custom NEXO_HOME path")
|
|
168
|
-
args = parser.parse_args()
|
|
169
|
-
|
|
170
|
-
nexo_home = Path(args.nexo_home) if args.nexo_home else Path.home() / ".nexo"
|
|
171
|
-
version = get_version()
|
|
172
|
-
shell_name, profile_path = detect_shell()
|
|
173
|
-
|
|
174
|
-
print(f"NEXO Brain Installer v{version}")
|
|
175
|
-
print(f"=" * 40)
|
|
176
|
-
print(f" NEXO_HOME: {nexo_home}")
|
|
177
|
-
print(f" Shell: {shell_name} ({profile_path})")
|
|
178
|
-
print(f" Version: {version}")
|
|
179
|
-
print()
|
|
180
|
-
|
|
181
|
-
if nexo_home.exists() and (nexo_home / "version.json").exists():
|
|
182
|
-
existing = json.loads((nexo_home / "version.json").read_text())
|
|
183
|
-
print(f" Existing installation found: v{existing.get('version', '?')}")
|
|
184
|
-
print(f" Use nexo-migrate.py to upgrade instead.")
|
|
185
|
-
if not args.yes:
|
|
186
|
-
resp = input(" Continue anyway? (y/N) ")
|
|
187
|
-
if resp.lower() != "y":
|
|
188
|
-
print("Aborted.")
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
if not args.yes:
|
|
192
|
-
resp = input("Install NEXO Brain? (Y/n) ")
|
|
193
|
-
if resp.lower() == "n":
|
|
194
|
-
print("Aborted.")
|
|
195
|
-
return
|
|
196
|
-
|
|
197
|
-
print()
|
|
198
|
-
print("Installing...")
|
|
199
|
-
|
|
200
|
-
# Step 1: Create directories
|
|
201
|
-
create_directory_structure(nexo_home)
|
|
202
|
-
|
|
203
|
-
# Step 2: Initialize databases
|
|
204
|
-
initialize_databases(nexo_home)
|
|
205
|
-
|
|
206
|
-
# Step 3: Copy files from repo
|
|
207
|
-
copy_repo_files(nexo_home)
|
|
208
|
-
|
|
209
|
-
# Step 4: Set NEXO_HOME in shell profile
|
|
210
|
-
set_nexo_home_env(nexo_home, shell_name, profile_path)
|
|
211
|
-
|
|
212
|
-
# Step 5: Write version.json
|
|
213
|
-
write_version_json(nexo_home, version)
|
|
214
|
-
|
|
215
|
-
print()
|
|
216
|
-
print("=" * 40)
|
|
217
|
-
print("NEXO Brain installed successfully!")
|
|
218
|
-
print()
|
|
219
|
-
print("Next steps:")
|
|
220
|
-
print(f" 1. Restart your shell or run: source {profile_path}")
|
|
221
|
-
print(f" 2. Add the NEXO MCP server to your Claude Code config:")
|
|
222
|
-
print(f" claude mcp add nexo -- python3 {REPO_ROOT / 'src' / 'server.py'}")
|
|
223
|
-
print(f" 3. Start a Claude Code session — NEXO will initialize automatically.")
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if __name__ == "__main__":
|
|
227
|
-
main()
|
|
4
|
+
print("This installer is deprecated. Please use: npx nexo-brain")
|
|
5
|
+
print("See: https://github.com/wazionapps/nexo#installation")
|
|
6
|
+
sys.exit(1)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
1
|
#!/usr/bin/env python3
|
|
3
2
|
"""NEXO Learning Housekeeping — Nightly dedup, weight adjustment, and review.
|
|
4
3
|
|
|
@@ -7,6 +6,7 @@ detects duplicates via semantic similarity, and archives stale learnings.
|
|
|
7
6
|
"""
|
|
8
7
|
|
|
9
8
|
import json
|
|
9
|
+
import os
|
|
10
10
|
import sqlite3
|
|
11
11
|
import sys
|
|
12
12
|
import time
|
|
@@ -14,10 +14,14 @@ from datetime import datetime, timedelta
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
17
|
+
# Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
|
|
18
|
+
_script_dir = Path(__file__).resolve().parent
|
|
19
|
+
_repo_src = _script_dir.parent # src/scripts/ -> src/
|
|
20
|
+
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(_repo_src) if (_repo_src / "server.py").exists() else str(NEXO_HOME)))
|
|
17
21
|
|
|
18
|
-
sys.path.insert(0, str(
|
|
22
|
+
sys.path.insert(0, str(NEXO_CODE))
|
|
19
23
|
|
|
20
|
-
DB_PATH = NEXO_HOME / "
|
|
24
|
+
DB_PATH = NEXO_HOME / "data" / "nexo.db"
|
|
21
25
|
STATE_FILE = NEXO_HOME / "operations" / ".catchup-state.json"
|
|
22
26
|
|
|
23
27
|
# Weight adjustment rates
|
|
@@ -30,7 +30,7 @@ from pathlib import Path
|
|
|
30
30
|
|
|
31
31
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
32
32
|
|
|
33
|
-
NEXO_DB =
|
|
33
|
+
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
34
34
|
CLAUDE_CLI = Path.home() / ".local" / "bin" / "claude"
|
|
35
35
|
|
|
36
36
|
|
|
@@ -111,27 +111,6 @@ Rules:
|
|
|
111
111
|
|
|
112
112
|
# Try CLI first, fall back to mechanical similarity
|
|
113
113
|
if CLAUDE_CLI.exists():
|
|
114
|
-
env = os.environ.copy()
|
|
115
|
-
env.pop("CLAUDECODE", None)
|
|
116
|
-
env.pop("CLAUDE_CODE", None)
|
|
117
|
-
|
|
118
|
-
try:
|
|
119
|
-
result = subprocess.run(
|
|
120
|
-
[str(CLAUDE_CLI), "-p", prompt, "--model", "opus",
|
|
121
|
-
"--allowedTools", "Read,Write,Edit,Glob,Grep"],
|
|
122
|
-
capture_output=True, text=True, timeout=60, env=env
|
|
123
|
-
)
|
|
124
|
-
if result.returncode == 0:
|
|
125
|
-
text = result.stdout.strip()
|
|
126
|
-
# Strip markdown fences if present
|
|
127
|
-
if "```json" in text:
|
|
128
|
-
text = text.split("```json")[1].split("```")[0]
|
|
129
|
-
elif "```" in text:
|
|
130
|
-
text = text.split("```")[1].split("```")[0]
|
|
131
|
-
return json.loads(text.strip())
|
|
132
|
-
except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception):
|
|
133
|
-
pass # Fall through to mechanical fallback
|
|
134
|
-
|
|
135
114
|
# Fallback: mechanical SequenceMatcher (original logic)
|
|
136
115
|
return _mechanical_validate(finding, learnings)
|
|
137
116
|
|
|
@@ -73,10 +73,16 @@ def backup_databases() -> str:
|
|
|
73
73
|
backup_dir = NEXO_HOME / "backups" / f"pre-migrate-{ts}"
|
|
74
74
|
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
if
|
|
78
|
-
for db_file in
|
|
76
|
+
data_dir = NEXO_HOME / "data"
|
|
77
|
+
if data_dir.exists():
|
|
78
|
+
for db_file in data_dir.glob("*.db*"):
|
|
79
79
|
shutil.copy2(db_file, backup_dir / db_file.name)
|
|
80
|
+
# Also check legacy db/ location
|
|
81
|
+
legacy_db_dir = NEXO_HOME / "db"
|
|
82
|
+
if legacy_db_dir.exists():
|
|
83
|
+
for db_file in legacy_db_dir.glob("*.db*"):
|
|
84
|
+
if not (backup_dir / db_file.name).exists():
|
|
85
|
+
shutil.copy2(db_file, backup_dir / db_file.name)
|
|
80
86
|
|
|
81
87
|
# Also backup version.json
|
|
82
88
|
vfile = NEXO_HOME / "version.json"
|
|
@@ -29,10 +29,14 @@ from pathlib import Path
|
|
|
29
29
|
HOME = Path.home()
|
|
30
30
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(HOME / ".nexo")))
|
|
31
31
|
|
|
32
|
-
# Add
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
# Add NEXO_HOME to path for cognitive engine (Stage 3)
|
|
33
|
+
# Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
|
|
34
|
+
_script_dir = Path(__file__).resolve().parent
|
|
35
|
+
_repo_src = _script_dir.parent # src/scripts/ -> src/
|
|
36
|
+
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(_repo_src) if (_repo_src / "server.py").exists() else str(NEXO_HOME)))
|
|
37
|
+
sys.path.insert(0, str(NEXO_CODE))
|
|
38
|
+
|
|
39
|
+
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
36
40
|
# Memory directory — adjust to match your project's memory location
|
|
37
41
|
MEMORY_DIR = NEXO_HOME / "memory"
|
|
38
42
|
MEMORY_INDEX = MEMORY_DIR / "MEMORY.md"
|
|
@@ -109,7 +113,7 @@ def consolidate_with_cli(data: dict) -> bool:
|
|
|
109
113
|
|
|
110
114
|
diaries_with_critique = [
|
|
111
115
|
d for d in data["diaries"]
|
|
112
|
-
if d.get("self_critique") and not (d["self_critique"]
|
|
116
|
+
if d.get("self_critique") and not str(d["self_critique"]).strip().lower().startswith("no self-critique")
|
|
113
117
|
]
|
|
114
118
|
|
|
115
119
|
if not diaries_with_critique:
|
|
@@ -121,8 +125,10 @@ def consolidate_with_cli(data: dict) -> bool:
|
|
|
121
125
|
if len(diaries_json) > 12000:
|
|
122
126
|
diaries_json = diaries_json[:12000] + "\n... (truncated)"
|
|
123
127
|
|
|
124
|
-
prompt = f"""
|
|
125
|
-
|
|
128
|
+
prompt = f"""FIRST: Call nexo_startup(task='nightly postmortem consolidation') to register this session.
|
|
129
|
+
|
|
130
|
+
You are NEXO's nightly consolidator. Your job is to review the self-critiques
|
|
131
|
+
from today and decide which deserve to become permanent rules. Use nexo_learning_add for permanent rules and nexo_followup_create for action items.
|
|
126
132
|
|
|
127
133
|
DATE: {data['date']}
|
|
128
134
|
SESSIONS TODAY: {len(data['diaries'])} total, {len(diaries_with_critique)} with self-critique
|
|
@@ -181,16 +187,17 @@ INSTRUCTIONS:
|
|
|
181
187
|
Execute without asking."""
|
|
182
188
|
|
|
183
189
|
log(f"Stage 2: Invoking Claude CLI (opus) with {len(diaries_with_critique)} critiques...")
|
|
184
|
-
|
|
185
190
|
env = os.environ.copy()
|
|
191
|
+
env["NEXO_HEADLESS"] = "1" # Skip stop hook post-mortem
|
|
186
192
|
env.pop("CLAUDECODE", None)
|
|
187
193
|
env.pop("CLAUDE_CODE", None)
|
|
188
194
|
|
|
189
195
|
try:
|
|
190
196
|
result = subprocess.run(
|
|
191
197
|
[str(CLAUDE_CLI), "-p", prompt, "--model", "opus",
|
|
192
|
-
"--
|
|
193
|
-
|
|
198
|
+
"--output-format", "text",
|
|
199
|
+
"--allowedTools", "Read,Write,Edit,Glob,Grep,Bash,mcp__nexo__*"],
|
|
200
|
+
capture_output=True, text=True, timeout=21600, env=env
|
|
194
201
|
)
|
|
195
202
|
|
|
196
203
|
if result.returncode != 0:
|
|
@@ -4,12 +4,14 @@ NEXO Pre-Commit Validator — Dynamic version
|
|
|
4
4
|
Queries nexo.db learnings to surface relevant warnings/blockers before commit.
|
|
5
5
|
Runs standalone (no MCP dependency, just sqlite3).
|
|
6
6
|
"""
|
|
7
|
+
import os
|
|
7
8
|
import sqlite3
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
14
|
+
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
def get_staged_files():
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# NEXO Prevent Sleep — keeps the machine awake so nocturnal processes run.
|
|
3
|
+
#
|
|
4
|
+
# macOS: uses caffeinate -s -i (prevent system + idle sleep)
|
|
5
|
+
# Linux: uses systemd-inhibit or caffeine if available, otherwise no-op
|
|
6
|
+
#
|
|
7
|
+
# Run as LaunchAgent (KeepAlive) or systemd service.
|
|
8
|
+
|
|
9
|
+
case "$(uname -s)" in
|
|
10
|
+
Darwin)
|
|
11
|
+
exec caffeinate -s -i -w $$
|
|
12
|
+
;;
|
|
13
|
+
Linux)
|
|
14
|
+
if command -v systemd-inhibit &>/dev/null; then
|
|
15
|
+
exec systemd-inhibit --what=idle:sleep --who=nexo --why="NEXO nocturnal processes" sleep infinity
|
|
16
|
+
elif command -v caffeine &>/dev/null; then
|
|
17
|
+
exec caffeine
|
|
18
|
+
else
|
|
19
|
+
echo "[NEXO] No sleep prevention tool found. Install systemd-inhibit or caffeine."
|
|
20
|
+
echo "[NEXO] Nocturnal processes may not run if the system sleeps."
|
|
21
|
+
# Keep running so launchd/systemd doesn't restart loop
|
|
22
|
+
exec sleep infinity
|
|
23
|
+
fi
|
|
24
|
+
;;
|
|
25
|
+
*)
|
|
26
|
+
echo "[NEXO] Unsupported platform: $(uname -s)"
|
|
27
|
+
exit 1
|
|
28
|
+
;;
|
|
29
|
+
esac
|
|
@@ -21,7 +21,7 @@ from pathlib import Path
|
|
|
21
21
|
|
|
22
22
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
23
23
|
|
|
24
|
-
NEXO_DB = NEXO_HOME / "
|
|
24
|
+
NEXO_DB = NEXO_HOME / "data" / "nexo.db"
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def get_db():
|
|
@@ -38,6 +38,7 @@ def check_overdue_followups() -> list[dict]:
|
|
|
38
38
|
SELECT id, description, date, created_at, reasoning
|
|
39
39
|
FROM followups
|
|
40
40
|
WHERE status NOT LIKE 'COMPLETED%'
|
|
41
|
+
AND status NOT IN ('DELETED','archived','blocked','waiting')
|
|
41
42
|
AND date IS NOT NULL AND date != ''
|
|
42
43
|
ORDER BY date ASC
|
|
43
44
|
""").fetchall()
|
|
@@ -151,7 +152,7 @@ def check_session_gaps() -> list[dict]:
|
|
|
151
152
|
def check_evolution_status() -> list[dict]:
|
|
152
153
|
"""Check if evolution system is healthy."""
|
|
153
154
|
alerts = []
|
|
154
|
-
obj_file = NEXO_HOME / "
|
|
155
|
+
obj_file = NEXO_HOME / "brain" / "evolution-objective.json"
|
|
155
156
|
if obj_file.exists():
|
|
156
157
|
obj = json.loads(obj_file.read_text())
|
|
157
158
|
if not obj.get("evolution_enabled", True):
|
|
@@ -219,7 +220,7 @@ def check_recurring_errors() -> list[dict]:
|
|
|
219
220
|
alerts.append({
|
|
220
221
|
"type": "recurring_errors",
|
|
221
222
|
"severity": "medium",
|
|
222
|
-
"title": f"
|
|
223
|
+
"title": f"Category '{r['category']}' has {r['cnt']} learnings this week — possible systemic issue",
|
|
223
224
|
"category": r["category"],
|
|
224
225
|
"count": r["cnt"],
|
|
225
226
|
})
|
|
@@ -231,7 +232,7 @@ def check_cron_health() -> list[dict]:
|
|
|
231
232
|
alerts = []
|
|
232
233
|
|
|
233
234
|
# Check backup cron
|
|
234
|
-
backup_dir = NEXO_HOME / "
|
|
235
|
+
backup_dir = NEXO_HOME / "backups"
|
|
235
236
|
if backup_dir.exists():
|
|
236
237
|
backups = sorted(backup_dir.glob("nexo-*.db"), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
237
238
|
if backups:
|
|
@@ -4,13 +4,14 @@ NEXO Runtime Preflight
|
|
|
4
4
|
|
|
5
5
|
Runs safe end-to-end smoke tests for Cortex and Evolution using a temporary
|
|
6
6
|
workspace and a copied SQLite database. No external API calls are performed.
|
|
7
|
-
Results are written to
|
|
7
|
+
Results are written to NEXO_HOME/logs/runtime-preflight-summary.json.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import importlib.util
|
|
13
13
|
import json
|
|
14
|
+
import os
|
|
14
15
|
import shutil
|
|
15
16
|
import sqlite3
|
|
16
17
|
import sys
|
|
@@ -20,13 +21,25 @@ from datetime import datetime
|
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
|
|
22
23
|
HOME = Path.home()
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(HOME / ".nexo")))
|
|
25
|
+
# Auto-detect: if running from repo (src/scripts/), use src/ as NEXO_CODE
|
|
26
|
+
_script_dir = Path(__file__).resolve().parent
|
|
27
|
+
_repo_src = _script_dir.parent # src/scripts/ -> src/
|
|
28
|
+
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(_repo_src) if (_repo_src / "server.py").exists() else str(NEXO_HOME)))
|
|
29
|
+
|
|
30
|
+
LOG_DIR = NEXO_HOME / "logs"
|
|
25
31
|
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
26
32
|
SUMMARY_FILE = LOG_DIR / "runtime-preflight-summary.json"
|
|
27
|
-
DB_FILE =
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
DB_FILE = NEXO_HOME / "data" / "nexo.db"
|
|
34
|
+
# Evolution config: NEXO_HOME/brain/ (canonical), NEXO_HOME/cortex/ (legacy fallback), NEXO_CODE (dev fallback)
|
|
35
|
+
def _find_evolution_file(name: str) -> Path:
|
|
36
|
+
for candidate in [NEXO_HOME / "brain" / name, NEXO_HOME / "cortex" / name, NEXO_CODE / name]:
|
|
37
|
+
if candidate.exists():
|
|
38
|
+
return candidate
|
|
39
|
+
return NEXO_HOME / "brain" / name # default canonical path
|
|
40
|
+
|
|
41
|
+
CORTEX_OBJECTIVE = _find_evolution_file("evolution-objective.json")
|
|
42
|
+
CORTEX_PROMPT = _find_evolution_file("evolution-prompt.md")
|
|
30
43
|
|
|
31
44
|
|
|
32
45
|
def _load_module(name: str, path: Path):
|
|
@@ -143,7 +156,7 @@ def main() -> int:
|
|
|
143
156
|
temp_db = temp_root / "nexo.db"
|
|
144
157
|
shutil.copy2(DB_FILE, temp_db)
|
|
145
158
|
|
|
146
|
-
temp_cortex_dir = temp_root / "
|
|
159
|
+
temp_cortex_dir = temp_root / "brain"
|
|
147
160
|
temp_logs_dir = temp_root / "logs"
|
|
148
161
|
temp_coord_dir = temp_root / "coordination"
|
|
149
162
|
temp_daily_dir = temp_root / "daily_summaries"
|
|
@@ -159,18 +172,23 @@ def main() -> int:
|
|
|
159
172
|
|
|
160
173
|
temp_objective = temp_cortex_dir / "evolution-objective.json"
|
|
161
174
|
temp_prompt = temp_cortex_dir / "evolution-prompt.md"
|
|
162
|
-
|
|
175
|
+
if CORTEX_OBJECTIVE.exists():
|
|
176
|
+
shutil.copy2(CORTEX_OBJECTIVE, temp_objective)
|
|
177
|
+
else:
|
|
178
|
+
# Create a minimal objective so evolution smoke tests can proceed
|
|
179
|
+
temp_objective.write_text(json.dumps({"evolution_enabled": True, "dimensions": {}}, indent=2))
|
|
163
180
|
if CORTEX_PROMPT.exists():
|
|
164
181
|
shutil.copy2(CORTEX_PROMPT, temp_prompt)
|
|
165
|
-
|
|
182
|
+
snapshot_restore = NEXO_CODE / "scripts" / "nexo-snapshot-restore.sh"
|
|
183
|
+
if snapshot_restore.exists():
|
|
184
|
+
shutil.copy2(snapshot_restore, temp_scripts_dir / "nexo-snapshot-restore.sh")
|
|
166
185
|
|
|
167
186
|
temp_api_key = temp_root / "anthropic-api-key.txt"
|
|
168
187
|
temp_api_key.write_text("smoke-test-key")
|
|
169
188
|
|
|
170
|
-
evolution_cycle = _load_module("evolution_cycle",
|
|
189
|
+
evolution_cycle = _load_module("evolution_cycle", NEXO_CODE / "evolution_cycle.py")
|
|
171
190
|
evolution_cycle.NEXO_DB = temp_db
|
|
172
|
-
evolution_cycle.
|
|
173
|
-
evolution_cycle.CORTEX_DIR = temp_cortex_dir
|
|
191
|
+
evolution_cycle.NEXO_HOME = temp_root
|
|
174
192
|
evolution_cycle.SANDBOX_DIR = temp_sandbox_dir
|
|
175
193
|
evolution_cycle.SNAPSHOTS_DIR = temp_snapshots_dir
|
|
176
194
|
evolution_cycle.OBJECTIVE_FILE = temp_objective
|
|
@@ -189,7 +207,7 @@ def main() -> int:
|
|
|
189
207
|
"restore_ok": restore_ok,
|
|
190
208
|
}
|
|
191
209
|
|
|
192
|
-
cortex = _load_module("
|
|
210
|
+
cortex = _load_module("cortex_plugin", NEXO_CODE / "plugins" / "cortex.py")
|
|
193
211
|
cortex.NEXO_DB = temp_db
|
|
194
212
|
cortex.DRY_RUN = True
|
|
195
213
|
cortex.BRIEFING_FILE = temp_cortex_dir / "briefing.json"
|
|
@@ -209,50 +227,36 @@ def main() -> int:
|
|
|
209
227
|
cortex._check_health_endpoints = lambda: []
|
|
210
228
|
cortex._call_anthropic = lambda prompt, model=None, max_tokens=4096: _fake_cortex_response(model or "")
|
|
211
229
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
"
|
|
224
|
-
|
|
225
|
-
"
|
|
226
|
-
"
|
|
230
|
+
# Smoke test: verify cortex plugin loads and has expected tools
|
|
231
|
+
cortex_path = NEXO_CODE / "plugins" / "cortex.py"
|
|
232
|
+
if cortex_path.exists():
|
|
233
|
+
import importlib.util
|
|
234
|
+
spec = importlib.util.spec_from_file_location("cortex_plugin", str(cortex_path))
|
|
235
|
+
cortex_mod = importlib.util.module_from_spec(spec)
|
|
236
|
+
spec.loader.exec_module(cortex_mod)
|
|
237
|
+
assert hasattr(cortex_mod, 'TOOLS'), "cortex plugin missing TOOLS"
|
|
238
|
+
tool_names = [t[1] for t in cortex_mod.TOOLS]
|
|
239
|
+
assert "nexo_cortex_check" in tool_names, "cortex plugin missing nexo_cortex_check tool"
|
|
240
|
+
else:
|
|
241
|
+
tool_names = ["cortex.py not found"]
|
|
242
|
+
summary["checks"]["cortex_plugin"] = {
|
|
243
|
+
"status": "pass",
|
|
244
|
+
"tools_found": tool_names,
|
|
227
245
|
}
|
|
228
246
|
|
|
229
|
-
runner
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
before_runner_logs = sqlite3.connect(str(temp_db)).execute("SELECT COUNT(*) FROM evolution_log").fetchone()[0]
|
|
244
|
-
runner.run()
|
|
245
|
-
conn = sqlite3.connect(str(temp_db))
|
|
246
|
-
after_runner_logs = conn.execute("SELECT COUNT(*) FROM evolution_log").fetchone()[0]
|
|
247
|
-
latest_cycle = conn.execute("SELECT MAX(cycle_number) FROM evolution_log").fetchone()[0]
|
|
248
|
-
conn.close()
|
|
249
|
-
if after_runner_logs <= before_runner_logs:
|
|
250
|
-
raise RuntimeError("standalone evolution runner did not log proposals")
|
|
251
|
-
summary["checks"]["standalone_runner"] = {
|
|
252
|
-
"evolution_logs_added": after_runner_logs - before_runner_logs,
|
|
253
|
-
"latest_cycle": latest_cycle,
|
|
254
|
-
"budget_file_written": runner.BUDGET_FILE.exists(),
|
|
255
|
-
}
|
|
247
|
+
# Evolution runner: component verification (not full execution — that needs CLI)
|
|
248
|
+
runner_path = NEXO_CODE / "scripts" / "nexo-evolution-run.py"
|
|
249
|
+
if runner_path.exists():
|
|
250
|
+
runner = _load_module("nexo_evolution_run", runner_path)
|
|
251
|
+
checks = {
|
|
252
|
+
"module_loads": True,
|
|
253
|
+
"has_run": hasattr(runner, 'run'),
|
|
254
|
+
"has_load_objective": hasattr(runner, 'load_objective'),
|
|
255
|
+
"has_get_week_data": hasattr(runner, 'get_week_data'),
|
|
256
|
+
}
|
|
257
|
+
summary["checks"]["standalone_runner"] = checks
|
|
258
|
+
else:
|
|
259
|
+
summary["checks"]["standalone_runner"] = {"status": "skip", "reason": "runner not found"}
|
|
256
260
|
|
|
257
261
|
summary["ok"] = True
|
|
258
262
|
_write_summary(summary)
|
|
@@ -4,7 +4,7 @@ import smtplib, sys
|
|
|
4
4
|
from email.mime.text import MIMEText
|
|
5
5
|
from email.utils import formataddr
|
|
6
6
|
|
|
7
|
-
def send(subject, body, to="
|
|
7
|
+
def send(subject, body, to="user@example.com", cc="user@example.com"):
|
|
8
8
|
msg = MIMEText(body, 'plain', 'utf-8')
|
|
9
9
|
msg['From'] = formataddr(('NEXO', 'nexo@example.com'))
|
|
10
10
|
msg['To'] = to
|