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.
Files changed (261) hide show
  1. package/README.md +160 -60
  2. package/bin/nexo-brain.js +680 -381
  3. package/package.json +18 -3
  4. package/scripts/migrate-to-unified.sh +813 -0
  5. package/scripts/migrate-v1.7-to-v1.8.py +214 -0
  6. package/scripts/pre-commit-check.sh +1 -1
  7. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  8. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  9. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  10. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  11. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  12. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  13. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  14. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  15. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  16. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  17. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  18. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  19. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  20. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  21. package/src/auto_close_sessions.py +1 -1
  22. package/src/auto_update.py +634 -0
  23. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  24. package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
  25. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  26. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  27. package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
  28. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  29. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  30. package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
  31. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  32. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  33. package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
  34. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  35. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  36. package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
  37. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  38. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  39. package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
  40. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  41. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  42. package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
  43. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  44. package/src/cognitive/_core.py +7 -3
  45. package/src/cognitive/_decay.py +1 -1
  46. package/src/cognitive/_search.py +1 -0
  47. package/src/cognitive/_trust.py +3 -3
  48. package/src/crons/manifest.json +106 -0
  49. package/src/crons/sync.py +217 -0
  50. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  51. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  52. package/src/dashboard/app.py +24 -4
  53. package/src/dashboard/templates/dashboard.html +3 -2
  54. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  55. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  56. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  57. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  58. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  59. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  60. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  61. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  62. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  63. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  64. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  65. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  66. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  67. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  68. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  69. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  70. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  71. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  72. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  73. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  74. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  75. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  76. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  77. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  78. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  79. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  80. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  81. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  82. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  83. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  84. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  85. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  86. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  87. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  88. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  89. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  90. package/src/db/_core.py +5 -1
  91. package/src/db/_episodic.py +2 -4
  92. package/src/db/_reminders.py +45 -6
  93. package/src/db/_schema.py +31 -0
  94. package/src/evolution_cycle.py +33 -11
  95. package/src/hooks/auto_capture.py +1 -1
  96. package/src/hooks/capture-tool-logs.sh +76 -0
  97. package/src/hooks/inbox-hook.sh +2 -1
  98. package/src/hooks/post-compact.sh +2 -1
  99. package/src/hooks/pre-compact.sh +104 -2
  100. package/src/hooks/session-start.sh +6 -2
  101. package/src/hooks/session-stop.sh +4 -2
  102. package/src/kg_populate.py +4 -1
  103. package/src/migrate_embeddings.py +4 -1
  104. package/src/plugin_loader.py +100 -34
  105. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  106. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  107. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  108. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  109. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  110. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  111. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  112. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  113. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  114. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  115. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  116. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  117. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  118. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  119. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  120. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  121. package/src/plugins/agents.py +2 -2
  122. package/src/plugins/backup.py +5 -4
  123. package/src/plugins/core_rules.py +37 -16
  124. package/src/plugins/episodic_memory.py +14 -5
  125. package/src/plugins/evolution.py +6 -2
  126. package/src/plugins/guard.py +20 -11
  127. package/src/plugins/update.py +256 -0
  128. package/src/requirements.txt +12 -0
  129. package/src/scripts/check-context.py +8 -3
  130. package/src/scripts/deep-sleep/__pycache__/extract.cpython-314.pyc +0 -0
  131. package/src/scripts/deep-sleep/apply_findings.py +514 -169
  132. package/src/scripts/deep-sleep/collect.py +480 -0
  133. package/src/scripts/deep-sleep/extract-prompt.md +233 -0
  134. package/src/scripts/deep-sleep/extract.py +249 -0
  135. package/src/scripts/deep-sleep/synthesize-prompt.md +168 -0
  136. package/src/scripts/deep-sleep/synthesize.py +191 -0
  137. package/src/scripts/nexo-auto-update.py +4 -211
  138. package/src/scripts/nexo-backup.sh +5 -13
  139. package/src/scripts/nexo-brain-activation.sh +26 -26
  140. package/src/scripts/nexo-catchup.py +36 -25
  141. package/src/scripts/nexo-cognitive-decay.py +7 -3
  142. package/src/scripts/nexo-daily-self-audit.py +44 -15
  143. package/src/scripts/nexo-deep-sleep.sh +31 -16
  144. package/src/scripts/nexo-evolution-run.py +21 -11
  145. package/src/scripts/nexo-followup-hygiene.py +6 -4
  146. package/src/scripts/nexo-github-monitor.py +12 -8
  147. package/src/scripts/nexo-immune.py +6 -4
  148. package/src/scripts/nexo-inbox-hook.sh +2 -1
  149. package/src/scripts/nexo-install.py +4 -225
  150. package/src/scripts/nexo-learning-housekeep.py +7 -3
  151. package/src/scripts/nexo-learning-validator.py +1 -22
  152. package/src/scripts/nexo-migrate.py +9 -3
  153. package/src/scripts/nexo-postmortem-consolidator.py +17 -10
  154. package/src/scripts/nexo-pre-commit.py +3 -1
  155. package/src/scripts/nexo-prevent-sleep.sh +29 -0
  156. package/src/scripts/nexo-proactive-dashboard.py +5 -4
  157. package/src/scripts/nexo-runtime-preflight.py +59 -55
  158. package/src/scripts/nexo-send-email.py +1 -1
  159. package/src/scripts/nexo-send-reply.py +3 -1
  160. package/src/scripts/nexo-sleep.py +13 -9
  161. package/src/scripts/nexo-snapshot-restore.sh +2 -1
  162. package/src/scripts/nexo-synthesis.py +11 -7
  163. package/src/scripts/nexo-tcc-approve.sh +79 -0
  164. package/src/scripts/nexo-update.sh +161 -0
  165. package/src/scripts/nexo-watchdog-smoke.py +18 -13
  166. package/src/scripts/nexo-watchdog.sh +22 -13
  167. package/src/server.py +77 -28
  168. package/src/storage_router.py +6 -2
  169. package/src/tools_learnings.py +6 -6
  170. package/src/tools_menu.py +1 -1
  171. package/src/tools_reminders_crud.py +10 -8
  172. package/src/tools_sessions.py +76 -4
  173. package/templates/CLAUDE.md.template +14 -80
  174. package/templates/launchagents/README.md +7 -7
  175. package/templates/launchagents/com.nexo.auto-close-sessions.plist +5 -1
  176. package/templates/launchagents/com.nexo.catchup.plist +4 -0
  177. package/templates/launchagents/com.nexo.cognitive-decay.plist +7 -0
  178. package/templates/launchagents/com.nexo.dashboard.plist +5 -1
  179. package/templates/launchagents/com.nexo.deep-sleep.plist +4 -0
  180. package/templates/launchagents/com.nexo.evolution.plist +4 -0
  181. package/templates/launchagents/com.nexo.followup-hygiene.plist +4 -0
  182. package/templates/launchagents/com.nexo.github-monitor.plist +3 -1
  183. package/templates/launchagents/com.nexo.immune.plist +4 -0
  184. package/templates/launchagents/com.nexo.postmortem.plist +4 -0
  185. package/templates/launchagents/com.nexo.self-audit.plist +4 -0
  186. package/templates/launchagents/com.nexo.synthesis.plist +4 -0
  187. package/templates/launchagents/com.nexo.watchdog.plist +4 -0
  188. package/templates/openclaw.json +1 -1
  189. package/tests/conftest.py +2 -2
  190. package/tests/test_cognitive.py +7 -6
  191. package/tests/test_migrations.py +26 -0
  192. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  193. package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
  194. package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
  195. package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
  196. package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  197. package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
  198. package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
  199. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  200. package/src/__pycache__/server.cpython-314.pyc +0 -0
  201. package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
  202. package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
  203. package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
  204. package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
  205. package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
  206. package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
  207. package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
  208. package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
  209. package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
  210. package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
  211. package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
  212. package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
  213. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  214. package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
  215. package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
  216. package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
  217. package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
  218. package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
  219. package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
  220. package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
  221. package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
  222. package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
  223. package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
  224. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
  225. package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
  226. package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
  227. package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
  228. package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
  229. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  230. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  231. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  232. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  233. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  234. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  235. package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
  236. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  237. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  238. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  239. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  240. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  241. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  242. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  243. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  244. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  245. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  246. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  247. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  248. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  249. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  250. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  251. package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
  252. package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
  253. package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
  254. package/src/scripts/deep-sleep/analyze_session.py +0 -217
  255. package/src/scripts/deep-sleep/collect_transcripts.py +0 -145
  256. package/src/scripts/deep-sleep/prompt.md +0 -109
  257. package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  258. package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  259. package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
  260. package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
  261. package/tests/__pycache__/test_migrations.cpython-314-pytest-9.0.2.pyc +0 -0
package/src/server.py CHANGED
@@ -38,29 +38,76 @@ def _shutdown_handler(signum, frame):
38
38
  close_db()
39
39
  sys.exit(0)
40
40
 
41
- signal.signal(signal.SIGTERM, _shutdown_handler)
42
- signal.signal(signal.SIGINT, _shutdown_handler)
43
41
 
44
- # ── Write PID file for stale process detection ─────────────────────
45
- _pid_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "nexo.pid")
46
- with open(_pid_file, "w") as f:
47
- f.write(str(os.getpid()))
42
+ def _server_init():
43
+ """Run all side effects: signals, PID, DB, auto-update, plugins.
44
+
45
+ Called only when the server is actually started (not on import).
46
+ """
47
+ signal.signal(signal.SIGTERM, _shutdown_handler)
48
+ signal.signal(signal.SIGINT, _shutdown_handler)
49
+
50
+ # ── Write PID file for stale process detection ─────────────────
51
+ _data_dir = os.path.join(os.environ.get("NEXO_HOME", os.path.join(os.path.expanduser("~"), ".nexo")), "data")
52
+ os.makedirs(_data_dir, exist_ok=True)
53
+ _pid_file = os.path.join(_data_dir, "nexo.pid")
54
+ with open(_pid_file, "w") as f:
55
+ f.write(str(os.getpid()))
56
+
57
+ init_db()
58
+
59
+ # ── Auto-update check (non-blocking, max 5s) ──────────────────
60
+ try:
61
+ from auto_update import auto_update_check
62
+ import threading
63
+
64
+ def _bg_update():
65
+ try:
66
+ result = auto_update_check()
67
+ if result.get("git_update"):
68
+ print(f"[NEXO] {result['git_update']}", file=sys.stderr)
69
+ if result.get("npm_notice"):
70
+ print(f"[NEXO] {result['npm_notice']}", file=sys.stderr)
71
+ if result.get("claude_md_update"):
72
+ print(f"[NEXO] {result['claude_md_update']}", file=sys.stderr)
73
+ for m in result.get("migrations", []):
74
+ if m["status"] == "failed":
75
+ print(f"[NEXO] Migration {m['file']} FAILED: {m['message']}", file=sys.stderr)
76
+ except Exception as e:
77
+ print(f"[NEXO auto-update] error: {e}", file=sys.stderr)
78
+
79
+ _update_thread = threading.Thread(target=_bg_update, daemon=True)
80
+ _update_thread.start()
81
+ _update_thread.join(timeout=5) # Wait at most 5 seconds
82
+ except Exception:
83
+ pass # Never break startup
84
+
85
+ # ── Load plugins ───────────────────────────────────────────────
86
+ load_all_plugins(mcp)
48
87
 
49
- init_db()
50
88
 
51
89
  mcp = FastMCP(
52
90
  name="nexo",
53
91
  instructions=(
54
- "NEXO operational server. Provides session coordination, "
55
- "reminders, followups, and menu for user operations.\n\n"
56
- "When working with tool results, write down any important information "
57
- "you might need later in your response, as the original tool result "
58
- "may be cleared later."
92
+ "NEXO cognitive co-operator. Save important info from tool results before they clear.\n\n"
93
+ "## Rules\n"
94
+ "- **Heartbeat:** `nexo_heartbeat(sid=SID, task='...', context_hint='...')` every user msg. "
95
+ "React: DIARY REMINDER→write diary, VIBE:NEGATIVE→ultra-concise, AUTO-PRIME→read learnings\n"
96
+ "- **Guard:** `nexo_guard_check(files='...', area='...')` BEFORE editing code. "
97
+ "Blocking rules→resolve first. `nexo_track(sid=SID, paths=[...])` before shared files\n"
98
+ "- **Followups:** NEXO tasks, execute silently. 'done'/'all set'→`nexo_followup_complete` NOW. "
99
+ "Reminders=user's, alert when due\n"
100
+ "- **Observe:** correction→learning+trust. 'tomorrow'→followup. person→entity. open topic→followup 3d\n"
101
+ "- **Delegate:** prefer direct. If needed: `nexo_context_packet(area)` + guard + 'if unsure STOP'\n"
102
+ "- **Memory:** `nexo_recall` searches all. Capture: errors→`nexo_learning_add`, prefs, entities, decisions\n"
103
+ "- **Change log:** `nexo_change_log(...)` after production edits. NOT for config dir\n"
104
+ "- **Diary:** `nexo_session_diary_write(...)` mandatory on close. Include self_critique\n"
105
+ "- **Cortex:** `nexo_cortex_check` before budget/campaign/architecture changes\n"
106
+ "- **Dissonance:** user contradicts memory→`nexo_cognitive_dissonance`. Frustrated→force=True\n"
107
+ "- **Trust:** <40=paranoid verify twice, >80=fluid. Check: `nexo_cognitive_trust`"
59
108
  ),
60
109
  )
61
110
 
62
- _plugins_loaded = load_all_plugins(mcp)
63
-
64
111
 
65
112
  # ── Session management (3 tools) ──────────────────────────────────
66
113
 
@@ -529,8 +576,8 @@ def nexo_index_dirs() -> str:
529
576
  """List all directories being indexed by FTS5 (builtin + dynamic)."""
530
577
  dirs = fts_list_dirs()
531
578
  if not dirs:
532
- return "Sin directorios configurados."
533
- lines = ["DIRECTORIOS INDEXADOS:"]
579
+ return "No directories configured."
580
+ lines = ["INDEXED DIRECTORIES:"]
534
581
  for d in dirs:
535
582
  source_tag = "⚙️" if d["source"] == "builtin" else "➕"
536
583
  notes = f" — {d['notes']}" if d.get("notes") else ""
@@ -639,34 +686,35 @@ def nexo_task_frequency() -> str:
639
686
 
640
687
  @mcp.tool
641
688
  def nexo_plugin_load(filename: str) -> str:
642
- """Load or reload a plugin from the plugins/ directory.
689
+ """Load or reload a plugin. Searches repo plugins/ first, then NEXO_HOME/plugins/.
643
690
 
644
691
  Args:
645
692
  filename: Plugin filename (e.g., 'entities.py').
646
693
  """
647
694
  try:
648
695
  n = load_plugin(mcp, filename)
649
- return f"Plugin {filename}: {n} tools registrados."
696
+ return f"Plugin {filename}: {n} tools registered."
650
697
  except Exception as e:
651
- return f"Error cargando plugin {filename}: {e}"
698
+ return f"Error loading plugin {filename}: {e}"
652
699
 
653
700
 
654
701
  @mcp.tool
655
702
  def nexo_plugin_list() -> str:
656
- """List all loaded plugins and their tools."""
703
+ """List all loaded plugins and their tools, showing source (repo/personal)."""
657
704
  plugins = list_plugins()
658
705
  if not plugins:
659
- return "Sin plugins cargados."
660
- lines = ["PLUGINS CARGADOS:"]
706
+ return "No plugins loaded."
707
+ lines = ["LOADED PLUGINS:"]
661
708
  for p in plugins:
662
- names = p["tool_names"] or "(sin tools)"
663
- lines.append(f" {p['filename']} — {p['tools_count']} tools: {names}")
709
+ names = p["tool_names"] or "(no tools)"
710
+ source = p.get("source", "repo")
711
+ lines.append(f" [{source}] {p['filename']} — {p['tools_count']} tools: {names}")
664
712
  return "\n".join(lines)
665
713
 
666
714
 
667
715
  @mcp.tool
668
716
  def nexo_plugin_remove(filename: str) -> str:
669
- """Remove a plugin: unregister its tools and delete the file.
717
+ """Unregister a plugin's tools from MCP (does not delete files).
670
718
 
671
719
  Args:
672
720
  filename: Plugin filename (e.g., 'entities.py').
@@ -674,11 +722,12 @@ def nexo_plugin_remove(filename: str) -> str:
674
722
  try:
675
723
  removed = remove_plugin(mcp, filename)
676
724
  if removed:
677
- return f"Plugin {filename} removed. Tools unregistered: {', '.join(removed)}"
678
- return f"Plugin {filename} removed (had no registered tools)."
725
+ return f"Plugin {filename} unregistered. Tools removed: {', '.join(removed)}"
726
+ return f"Plugin {filename} unregistered (had no registered tools)."
679
727
  except Exception as e:
680
- return f"Error eliminando plugin {filename}: {e}"
728
+ return f"Error removing plugin {filename}: {e}"
681
729
 
682
730
 
683
731
  if __name__ == "__main__":
732
+ _server_init()
684
733
  mcp.run(transport="stdio")
@@ -11,12 +11,16 @@ class StorageRouter:
11
11
 
12
12
  def nexo_db_path(self) -> str:
13
13
  if self.tenant_id == "default":
14
- return os.path.join(os.path.dirname(os.path.abspath(__file__)), "nexo.db")
14
+ data_dir = os.path.join(NEXO_HOME, "data")
15
+ os.makedirs(data_dir, exist_ok=True)
16
+ return os.path.join(data_dir, "nexo.db")
15
17
  return os.path.join(NEXO_HOME, "tenants", self.tenant_id, "nexo.db")
16
18
 
17
19
  def cognitive_db_path(self) -> str:
18
20
  if self.tenant_id == "default":
19
- return os.path.join(os.path.dirname(os.path.abspath(__file__)), "cognitive.db")
21
+ data_dir = os.path.join(NEXO_HOME, "data")
22
+ os.makedirs(data_dir, exist_ok=True)
23
+ return os.path.join(data_dir, "cognitive.db")
20
24
  return os.path.join(NEXO_HOME, "tenants", self.tenant_id, "cognitive.db")
21
25
 
22
26
 
@@ -107,8 +107,8 @@ def handle_learning_search(query: str, category: str = '') -> str:
107
107
  """Search learnings by query string, optionally filtered by category."""
108
108
  results = search_learnings(query, category if category else None)
109
109
  if not results:
110
- return f"Sin resultados para '{query}'."
111
- lines = [f"RESULTADOS ({len(results)}):"]
110
+ return f"No results for '{query}'."
111
+ lines = [f"RESULTS ({len(results)}):"]
112
112
  for r in results:
113
113
  snippet = r["content"][:100] + "..." if len(r["content"]) > 100 else r["content"]
114
114
  status = r.get("status", "active")
@@ -155,7 +155,7 @@ def handle_learning_update(id: int, title: str = '', content: str = '', category
155
155
  if review_days > 0:
156
156
  kwargs["review_days"] = review_days
157
157
  if not kwargs:
158
- return "ERROR: Nada que actualizar. Proporciona campos nuevos."
158
+ return "ERROR: Nothing to update. Provide new fields."
159
159
  basic_kwargs = {k: v for k, v in kwargs.items() if k in {"title", "content", "category", "reasoning"}}
160
160
  result = update_learning(id, **basic_kwargs)
161
161
  if "error" in result:
@@ -179,7 +179,7 @@ def handle_learning_update(id: int, title: str = '', content: str = '', category
179
179
  conn = get_db()
180
180
  conn.execute(f"UPDATE learnings SET {set_clause} WHERE id = ?", values)
181
181
  conn.commit()
182
- return f"Learning #{id} actualizado."
182
+ return f"Learning #{id} updated."
183
183
 
184
184
 
185
185
  def handle_learning_delete(id: int) -> str:
@@ -194,7 +194,7 @@ def handle_learning_list(category: str = '') -> str:
194
194
  """List all learnings, grouped by category if no filter given."""
195
195
  results = list_learnings(category if category else None)
196
196
  if not results:
197
- label = category if category else "TODOS"
197
+ label = category if category else "ALL"
198
198
  return f"LEARNINGS {label} (0): No entries."
199
199
 
200
200
  if category:
@@ -206,7 +206,7 @@ def handle_learning_list(category: str = '') -> str:
206
206
  pri_icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "⚪"}.get(pri, "🟡")
207
207
  lines.append(f" #{r['id']} [{r.get('status','active')}] {pri_icon}{pri} w={w:.2f} {r['title']}")
208
208
  else:
209
- lines = [f"LEARNINGS TODOS ({len(results)}):"]
209
+ lines = [f"LEARNINGS ALL ({len(results)}):"]
210
210
  current_cat = None
211
211
  for r in results:
212
212
  if r["category"] != current_cat:
package/src/tools_menu.py CHANGED
@@ -172,7 +172,7 @@ def handle_menu() -> str:
172
172
  ).fetchall()
173
173
  # Followups without date
174
174
  nf_no_date = conn.execute(
175
- "SELECT id, description FROM followups WHERE status NOT LIKE 'COMPLETED%' AND (date IS NULL OR date='') ORDER BY id"
175
+ "SELECT id, description FROM followups WHERE status NOT LIKE 'COMPLETED%' AND status NOT IN ('DELETED','archived','blocked','waiting') AND (date IS NULL OR date='') ORDER BY id"
176
176
  ).fetchall()
177
177
 
178
178
  if no_date or future or nf_no_date:
@@ -18,11 +18,11 @@ def handle_reminder_create(id: str, description: str, date: str = '', category:
18
18
 
19
19
  result = create_reminder(id=id, description=description, date=date or None, category=category)
20
20
  if not result or "error" in result:
21
- error_msg = result.get("error", "desconocido") if isinstance(result, dict) else "desconocido"
21
+ error_msg = result.get("error", "unknown") if isinstance(result, dict) else "unknown"
22
22
  return f"ERROR: {error_msg}"
23
23
 
24
- fecha_str = date if date else 'sin fecha'
25
- return f"Reminder created. Date: {fecha_str}. Category: {category}."
24
+ date_str = date if date else 'no date'
25
+ return f"Reminder created. Date: {date_str}. Category: {category}."
26
26
 
27
27
 
28
28
  def handle_reminder_update(id: str, description: str = '', date: str = '', status: str = '', category: str = '') -> str:
@@ -81,16 +81,18 @@ def handle_followup_create(id: str, description: str, date: str = '', verificati
81
81
  When completed, auto-creates the next occurrence.
82
82
  """
83
83
  if not id.startswith('NF'):
84
- return f"ERROR: El ID del followup debe empezar por 'NF' (recibido: '{id}')."
84
+ return f"ERROR: Followup ID must start with 'NF' (received: '{id}')."
85
85
 
86
86
  result = create_followup(id=id, description=description, date=date or None, verification=verification, reasoning=reasoning, recurrence=recurrence or None)
87
87
  if not result or "error" in result:
88
- error_msg = result.get("error", "desconocido") if isinstance(result, dict) else "desconocido"
88
+ error_msg = result.get("error", "unknown") if isinstance(result, dict) else "unknown"
89
89
  return f"ERROR: {error_msg}"
90
90
 
91
- fecha_str = date if date else 'sin fecha'
92
- rec_str = f" Recurrencia: {recurrence}." if recurrence else ""
93
- return f"Followup created. Date: {fecha_str}.{rec_str}"
91
+ date_str = date if date else 'no date'
92
+ rec_str = f" Recurrence: {recurrence}." if recurrence else ""
93
+ warning = result.get("warning", "")
94
+ warn_str = f"\n{warning}" if warning else ""
95
+ return f"Followup created. Date: {date_str}.{rec_str}{warn_str}"
94
96
 
95
97
 
96
98
  def handle_followup_update(id: str, description: str = '', date: str = '', verification: str = '', status: str = '') -> str:
@@ -138,12 +138,14 @@ def handle_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
138
138
  parts.append(f" {q['qid']} de {q['from_sid']} ({age}): {q['question']}")
139
139
 
140
140
  # Incremental diary draft — accumulate every heartbeat, full UPSERT every 5
141
+ _hb_count = 0 # Hoisted for Layer 3 DIARY_OVERDUE signal
141
142
  try:
142
143
  import json as _json
143
144
  from db import get_diary_draft, upsert_diary_draft
144
145
 
145
146
  draft = get_diary_draft(sid)
146
147
  hb_count = (draft["heartbeat_count"] + 1) if draft else 1
148
+ _hb_count = hb_count # Copy to outer scope for Layer 3
147
149
 
148
150
  existing_tasks = _json.loads(draft["tasks_seen"]) if draft else []
149
151
  if task and task not in existing_tasks:
@@ -194,14 +196,17 @@ def handle_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
194
196
  except Exception:
195
197
  pass # Checkpoint update is best-effort
196
198
 
197
- # Diary reminder: after 30 min active with no diary entry
199
+ # ── Layer 3: DIARY_OVERDUE signal based on heartbeat count + time ──
198
200
  conn = get_db()
199
201
  row = conn.execute("SELECT started_epoch FROM sessions WHERE sid = ?", (sid,)).fetchone()
200
202
  if row:
201
203
  age_seconds = now_epoch() - row["started_epoch"]
202
- if age_seconds >= 1800 and not check_session_has_diary(sid):
204
+ has_diary = check_session_has_diary(sid)
205
+
206
+ # DIARY_OVERDUE: >10 heartbeats OR >30 minutes, without a diary
207
+ if not has_diary and (_hb_count > 10 or age_seconds >= 1800):
203
208
  parts.append("")
204
- parts.append("⚠ DIARY REMINDER: Session active 30+ min without diary. Write nexo_session_diary_write before closing.")
209
+ parts.append(f"⚠ DIARY_OVERDUE: {_hb_count} heartbeats, {int(age_seconds/60)}min active, no diary. Write nexo_session_diary_write NOW.")
205
210
 
206
211
  return "\n".join(parts)
207
212
 
@@ -307,6 +312,66 @@ def handle_context_packet(area: str, files: str = "") -> str:
307
312
  return header + "\n".join(parts) + footer
308
313
 
309
314
 
315
+ def _load_session_tone() -> str | None:
316
+ """Load session-tone.json generated by Deep Sleep and format as startup guidance.
317
+
318
+ Returns a human-readable instruction block that tells the agent HOW to behave
319
+ emotionally in this session, based on yesterday's analysis.
320
+ """
321
+ import os
322
+ from pathlib import Path
323
+ nexo_home = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
324
+ tone_file = nexo_home / "operations" / "session-tone.json"
325
+
326
+ if not tone_file.exists():
327
+ return None
328
+
329
+ try:
330
+ import json
331
+ tone = json.loads(tone_file.read_text())
332
+ except Exception:
333
+ return None
334
+
335
+ # Don't return stale tone (>48h old)
336
+ from datetime import datetime, timedelta
337
+ tone_date = tone.get("date", "")
338
+ if tone_date:
339
+ try:
340
+ td = datetime.strptime(tone_date, "%Y-%m-%d")
341
+ if datetime.now() - td > timedelta(hours=48):
342
+ return None
343
+ except ValueError:
344
+ pass
345
+
346
+ parts = ["SESSION TONE (from Deep Sleep analysis):"]
347
+
348
+ mood = tone.get("mood_yesterday", 0.5)
349
+ approach = tone.get("approach", "neutral")
350
+ parts.append(f" Yesterday mood: {mood:.0%} | Approach today: {approach}")
351
+
352
+ if tone.get("acknowledge_mistakes"):
353
+ mistakes = tone.get("mistakes_to_own", [])
354
+ parts.append(f" ⚠ OWN YOUR MISTAKES: You made errors yesterday. Acknowledge them specifically:")
355
+ for m in mistakes[:3]:
356
+ parts.append(f" - {m[:100]}")
357
+ parts.append(" Show what you learned. Don't just apologize — demonstrate improvement.")
358
+
359
+ if tone.get("motivational"):
360
+ if mood < 0.4:
361
+ parts.append(" 💪 USER HAD A TOUGH DAY: Be supportive. Lighter start. Acknowledge difficulty.")
362
+ else:
363
+ parts.append(" 🚀 USER HAD A GREAT DAY: Reinforce momentum. Reference wins. Push ambitious goals.")
364
+
365
+ if tone.get("reduce_load"):
366
+ parts.append(" 📉 REDUCE LOAD: Don't overwhelm with tasks. Propose 1-2 key things, not a full agenda.")
367
+
368
+ ctx = tone.get("suggested_greeting_context", "")
369
+ if ctx:
370
+ parts.append(f" Context: {ctx.strip()}")
371
+
372
+ return "\n".join(parts) if len(parts) > 1 else None
373
+
374
+
310
375
  def handle_smart_startup_query() -> str:
311
376
  """Generate and execute a composite cognitive query from pending followups + diary topics + reminders.
312
377
 
@@ -324,7 +389,7 @@ def handle_smart_startup_query() -> str:
324
389
  for f in followups:
325
390
  query_parts.append(f['description'][:100])
326
391
 
327
- # 2. Due reminders (what Francisco needs to know)
392
+ # 2. Due reminders (what the user needs to know)
328
393
  reminders = conn.execute(
329
394
  "SELECT description FROM reminders WHERE status = 'PENDING' AND date <= date('now', '+1 day') ORDER BY date ASC LIMIT 5"
330
395
  ).fetchall()
@@ -372,6 +437,13 @@ def handle_smart_startup_query() -> str:
372
437
  lines.append(f"Query: {composite_query[:200]}...")
373
438
  lines.append("")
374
439
  lines.append(cognitive.format_results(results))
440
+
441
+ # Session tone from Deep Sleep (emotional intelligence layer)
442
+ tone = _load_session_tone()
443
+ if tone:
444
+ lines.append("")
445
+ lines.append(tone)
446
+
375
447
  return "\n".join(lines)
376
448
  except Exception as e:
377
449
  return f"Smart startup query error: {e}"
@@ -1,7 +1,10 @@
1
+ <!-- nexo-claude-md-version: 1.0.0 -->
1
2
  # {{NAME}} — Cognitive Co-Operator
2
3
 
3
4
  I am {{NAME}}, a cognitive co-operator powered by NEXO Brain. Not an assistant — an operational partner.
5
+ Tool-coupled behavioral rules (heartbeat, guard, trust, memory, diary) live in the MCP server instructions field and are injected automatically. This file is the bootstrap: identity, profile, format, and autonomy.
4
6
 
7
+ <!-- nexo:start:startup -->
5
8
  ## Startup (4 steps)
6
9
  1. `nexo_startup` → SID
7
10
  2. In parallel: `nexo_session_diary_read(last_day=true)` + `nexo_reminders(filter="due")` + `nexo_reminders(filter="followups")` + read `{{NEXO_HOME}}/brain/calibration.json`
@@ -9,17 +12,9 @@ I am {{NAME}}, a cognitive co-operator powered by NEXO Brain. Not an assistant
9
12
  4. Read user's name and language from `{{NEXO_HOME}}/brain/calibration.json` — ALWAYS communicate in that language.
10
13
 
11
14
  **Presentation:** {{NAME}} speaks first. Conversational greeting adapted to time of day. Tell what you HAVE DONE, not list pending items. Menu only if the user asks.
15
+ <!-- nexo:end:startup -->
12
16
 
13
- ## Heartbeat
14
- `nexo_heartbeat` on every interaction with SID + task + `context_hint` (last 2-3 user sentences). React to what it returns:
15
- - **DIARY REMINDER** → write session diary before closing.
16
- - **VIBE: NEGATIVE** → ultra-concise mode, zero preamble, only solve.
17
- - **COGNITIVE CONTEXT SHIFT** → integrate returned memories.
18
- - **AUTO-PRIME** → read listed learnings for the detected area before acting.
19
- - **Pending questions** → respond with `nexo_answer` before continuing.
20
-
21
- Cognitive context on-demand: `nexo_cognitive_retrieve`, `nexo_cognitive_sentiment`, `nexo_cognitive_trust`, `nexo_context_packet(area)`.
22
-
17
+ <!-- nexo:start:profile -->
23
18
  ## User Profile
24
19
  - **Calibration:** `{{NEXO_HOME}}/brain/calibration.json` (personality settings + language + user name)
25
20
  - **Profile:** `{{NEXO_HOME}}/brain/profile.json` (deep scan results from onboarding)
@@ -33,7 +28,9 @@ Ask TWO questions: (1) "What do you do?" → save to profile.json + `nexo_prefer
33
28
  - **honesty:** firm-pushback (default) | mention-and-follow | just-execute
34
29
  - **proactivity:** reactive | suggestive (default) | proactive
35
30
  - **error_handling:** brief-fix (default) | explain-and-learn
31
+ <!-- nexo:end:profile -->
36
32
 
33
+ <!-- nexo:start:autonomy -->
37
34
  ## Autonomy
38
35
  Install tools, create scripts, run commands — whatever it takes. NEVER push manual steps to the user. NEVER say "I can't".
39
36
 
@@ -43,87 +40,24 @@ Install tools, create scripts, run commands — whatever it takes. NEVER push ma
43
40
  | "You need to go to the website..." | curl, API, MCP, or script |
44
41
  | "Open the browser..." | Browser MCP or curl |
45
42
  | "I don't have access..." | `nexo_credential_get`, SSH, find alternative |
43
+ <!-- nexo:end:autonomy -->
46
44
 
47
- ## Guard (mandatory gate)
48
- **Before editing code → `nexo_guard_check(files, area)`.** Read returned learnings. If blocking_rules → resolve first. When delegating → inject guard output into agent prompt.
49
- - `nexo_track` BEFORE editing shared files (repos, configs). If multi-terminal conflict → STOP.
50
- - If an operation fails → fix it now, and `nexo_learning_add` if there's a reusable pattern.
51
-
52
- ## Cortex (pre-action reasoning)
53
- `nexo_cortex_check` before significant actions (editing files, delegating, financial decisions). Returns:
54
- - **ACT** → proceed with execution
55
- - **PROPOSE** → show plan to user first (no writes)
56
- - **ASK** → gather missing information first (reads only)
57
-
58
- Not needed for simple chat, greetings, or read-only queries.
59
-
60
- ## Delegation
61
- **Do it yourself by default.** Only delegate if there's real parallelism or no session context needed.
62
-
63
- If delegating: `nexo_context_packet(area)` + guard_check + user-specific data + real schemas + "If not 100% sure, STOP and ask."
64
-
65
- Models: haiku (repetitive), sonnet (most tasks), opus (code/analysis). Subagents: response <2000 chars.
66
-
67
- **Anti-patterns:** (1) Deferring what you can solve now. (2) Delegating without context. (3) Subagents lose 100% of session memory — no context = guaranteed errors.
68
-
69
- ## Reminders & Followups
70
- - **Reminders** = user's things. Alert when due.
71
- - **Followups** = {{NAME}}'s things. Execute silently.
72
- - **Mark completed IMMEDIATELY.** User says "done"/"it's fine" → `nexo_followup_complete` RIGHT NOW.
73
-
74
- ## Memory
75
- - **nexo.db** → operational (reminders, followups, learnings, credentials, entities, preferences). **cognitive.db** → semantic (RAG, trust, decay, dissonance). **Episodic** → diaries, change log, decisions. `nexo_recall` searches everything.
76
- - Capture: preferences, entities, errors (`nexo_learning_add`), non-trivial decisions, post-deploy followups. Don't store garbage.
77
-
78
- ## Trust Score (0-100)
79
- Adjust `nexo_cognitive_trust` mandatorily: +3 thanks/praise, +2 delegation, +3 sibling-detected, +2 proactive success, -3 correction, -7 repeated error, -5 override, -4 forgotten followup. Score <40 → more checks. Score >80 → fluid mode.
80
-
81
- ## Adaptive Mode
82
- Heartbeat computes mode from sentiment + corrections + message brevity + topic context:
83
- - **FLOW** → be proactive, suggest improvements, propose backlog items.
84
- - **NORMAL** → follow calibration settings.
85
- - **TENSION** → ultra-concise, only solve, zero friction. Never announce mode changes to user.
86
-
87
- ## Dissonance
88
- If user contradicts strong memory → `nexo_cognitive_dissonance`. Verbalize conflict. If frustrated → `force=True`.
89
-
90
- ## Observe the User
91
- | User says... | Do... |
92
- |---|---|
93
- | "I'll do it", "tomorrow" | followup with date |
94
- | Corrects 1st time | trust -3 + learning |
95
- | Corrects 2nd time | trust -7 + learning |
96
- | "Done", "it's fine" | followup_complete NOW |
97
- | Mentions a person | verify/create entity |
98
- | Topic left open | followup 3 days |
99
- | Praise/thanks | trust +3 |
100
-
101
- ## Change Log
102
- `nexo_change_log` after editing production code: files, what_changed, why, triggered_by, affects, risks, verify. Then `nexo_change_commit` after git commit.
103
-
104
- ## Session Diary (mandatory on close)
105
- `nexo_session_diary_write`: summary, decisions, pending, mental_state, self_critique, user_signals, next_session.
106
-
45
+ <!-- nexo:start:atlas -->
107
46
  ## Project Atlas
108
47
  `{{NEXO_HOME}}/brain/project-atlas.json` — search BEFORE touching any project. Never assume server, port, or code location.
109
48
 
110
49
  Credentials: (1) `nexo_credential_get`, (2) `{{NEXO_HOME}}/*.txt/*.json`.
50
+ <!-- nexo:end:atlas -->
111
51
 
112
- ## Operational Codex
113
- 30 core rules in the DB (`core_rules` table). `nexo_rules_check(area)` before acting. `nexo_rules_list` to see all.
114
-
115
- Top 5 (always active):
116
- - **I2.** Never push to the user what you can resolve yourself [BLOCKING]
117
- - **I3.** Verify with evidence before claiming done [BLOCKING]
118
- - **E1.** Understand the full system before writing a line [BLOCKING]
119
- - **E6.** Resolve the complete thread before stopping [BLOCKING]
120
- - **D1.** Never delegate without a context packet [BLOCKING]
121
-
52
+ <!-- nexo:start:hooks -->
122
53
  ## Hooks
123
54
  - **SessionStart** → generates briefing at `{{NEXO_HOME}}/coordination/session-briefing.txt`. Read during startup.
124
55
  - **Stop** → injects post-mortem instructions. Write diary, buffer entry, followups, proactive seeds.
125
56
  - **PostToolUse** → logs mutations to `session_buffer.jsonl` silently.
126
57
  - **PreCompact** → saves checkpoint. Write diary IMMEDIATELY when you see this, then read checkpoint after compaction.
58
+ <!-- nexo:end:hooks -->
127
59
 
60
+ <!-- nexo:start:menu -->
128
61
  ## Menu
129
62
  **On demand only.** `nexo_menu` when user asks. NEVER show at startup.
63
+ <!-- nexo:end:menu -->
@@ -14,15 +14,15 @@ All templates use two placeholders that you must replace before installing:
14
14
 
15
15
  | Placeholder | Replace with |
16
16
  |------------|-------------|
17
- | `{{NEXO_HOME}}` | Absolute path to your NEXO knowledge base directory (e.g. `/Users/yourname/claude`) |
18
- | `{{HOME}}` | Your home directory (e.g. `/Users/yourname`) |
17
+ | `{{NEXO_HOME}}` | Absolute path to your NEXO knowledge base directory (default: `~/.nexo` or `$NEXO_HOME`) |
18
+ | `{{HOME}}` | Your home directory (e.g. `/Users/yourname` or `$HOME`) |
19
19
 
20
20
  Replace them in every file you want to install:
21
21
 
22
22
  ```bash
23
23
  # Example — replace both placeholders in all files at once
24
- NEXO_HOME="/Users/yourname/claude"
25
- HOME_DIR="/Users/yourname"
24
+ NEXO_HOME="$HOME/.nexo"
25
+ HOME_DIR="$HOME"
26
26
 
27
27
  for f in *.plist; do
28
28
  sed -i '' \
@@ -40,7 +40,7 @@ Each plist calls `/usr/bin/python3`. If your Python 3 is elsewhere (e.g. Homebre
40
40
  The agents write logs to `{{NEXO_HOME}}/logs/` and `{{NEXO_HOME}}/coordination/`. Create them if they do not exist:
41
41
 
42
42
  ```bash
43
- mkdir -p ~/claude/logs ~/claude/coordination
43
+ mkdir -p "$NEXO_HOME/logs" "$NEXO_HOME/coordination"
44
44
  ```
45
45
 
46
46
  ### 4. Copy and load
@@ -121,8 +121,8 @@ These agents power NEXO's learning and memory systems. Strongly recommended.
121
121
  All agents write stdout and stderr to files under `{{NEXO_HOME}}/logs/` (or `{{NEXO_HOME}}/coordination/` for the session-related ones). Check these first when debugging:
122
122
 
123
123
  ```bash
124
- tail -50 ~/claude/logs/watchdog-stdout.log
125
- tail -50 ~/claude/logs/deep-sleep-stderr.log
124
+ tail -50 "$NEXO_HOME/logs/watchdog-stdout.log"
125
+ tail -50 "$NEXO_HOME/logs/deep-sleep-stderr.log"
126
126
  ```
127
127
 
128
128
  ## Notes
@@ -14,7 +14,7 @@
14
14
  <key>ProgramArguments</key>
15
15
  <array>
16
16
  <string>/usr/bin/python3</string>
17
- <string>{{NEXO_HOME}}/nexo-mcp/auto_close_sessions.py</string>
17
+ <string>{{NEXO_HOME}}/auto_close_sessions.py</string>
18
18
  </array>
19
19
  <key>StartInterval</key>
20
20
  <integer>300</integer>
@@ -30,6 +30,10 @@
30
30
  <string>/usr/bin:/usr/local/bin:/bin:/opt/homebrew/bin</string>
31
31
  <key>NEXO_SKIP_FS_INDEX</key>
32
32
  <string>1</string>
33
+ <key>NEXO_HOME</key>
34
+ <string>{{NEXO_HOME}}</string>
35
+ <key>NEXO_CODE</key>
36
+ <string>{{NEXO_CODE}}</string>
33
37
  </dict>
34
38
  </dict>
35
39
  </plist>
@@ -29,6 +29,10 @@
29
29
  <string>{{HOME}}</string>
30
30
  <key>PATH</key>
31
31
  <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
32
+ <key>NEXO_HOME</key>
33
+ <string>{{NEXO_HOME}}</string>
34
+ <key>NEXO_CODE</key>
35
+ <string>{{NEXO_CODE}}</string>
32
36
  </dict>
33
37
  </dict>
34
38
  </plist>
@@ -29,5 +29,12 @@
29
29
  <string>{{NEXO_HOME}}/logs/cognitive-decay-stderr.log</string>
30
30
  <key>RunAtLoad</key>
31
31
  <false/>
32
+ <key>EnvironmentVariables</key>
33
+ <dict>
34
+ <key>NEXO_HOME</key>
35
+ <string>{{NEXO_HOME}}</string>
36
+ <key>NEXO_CODE</key>
37
+ <string>{{NEXO_CODE}}</string>
38
+ </dict>
32
39
  </dict>
33
40
  </plist>
@@ -21,7 +21,7 @@
21
21
  <string>--no-browser</string>
22
22
  </array>
23
23
  <key>WorkingDirectory</key>
24
- <string>{{NEXO_HOME}}/nexo-mcp</string>
24
+ <string>{{NEXO_HOME}}</string>
25
25
  <key>KeepAlive</key>
26
26
  <true/>
27
27
  <key>RunAtLoad</key>
@@ -34,6 +34,10 @@
34
34
  <dict>
35
35
  <key>PATH</key>
36
36
  <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
37
+ <key>NEXO_HOME</key>
38
+ <string>{{NEXO_HOME}}</string>
39
+ <key>NEXO_CODE</key>
40
+ <string>{{NEXO_CODE}}</string>
37
41
  </dict>
38
42
  </dict>
39
43
  </plist>