nexo-brain 2.2.0 → 2.3.1

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 (256) hide show
  1. package/README.md +5 -5
  2. package/package.json +6 -3
  3. package/src/auto_update.py +26 -0
  4. package/src/crons/manifest.json +6 -13
  5. package/src/crons/sync.py +150 -6
  6. package/src/db/__init__.py +13 -0
  7. package/src/db/_core.py +1 -0
  8. package/src/db/_cron_runs.py +74 -0
  9. package/src/db/_entities.py +1 -0
  10. package/src/db/_episodic.py +41 -6
  11. package/src/db/_learnings.py +1 -0
  12. package/src/db/_reminders.py +1 -0
  13. package/src/db/_schema.py +64 -0
  14. package/src/db/_sessions.py +1 -0
  15. package/src/db/_skills.py +515 -0
  16. package/src/hooks/session-stop.sh +13 -101
  17. package/src/plugin_loader.py +1 -0
  18. package/src/plugins/episodic_memory.py +5 -3
  19. package/src/plugins/schedule.py +212 -0
  20. package/src/plugins/skills.py +264 -0
  21. package/src/plugins/update.py +1 -0
  22. package/src/scripts/deep-sleep/apply_findings.py +111 -8
  23. package/src/scripts/deep-sleep/collect.py +34 -11
  24. package/src/scripts/deep-sleep/extract-prompt.md +38 -0
  25. package/src/scripts/deep-sleep/extract.py +81 -8
  26. package/src/scripts/deep-sleep/synthesize-prompt.md +29 -1
  27. package/src/scripts/deep-sleep/synthesize.py +4 -1
  28. package/src/scripts/nexo-catchup.py +65 -29
  29. package/src/scripts/nexo-cron-wrapper.sh +53 -0
  30. package/src/scripts/nexo-daily-self-audit.py +4 -2
  31. package/src/scripts/nexo-deep-sleep.sh +66 -77
  32. package/src/scripts/nexo-evolution-run.py +13 -0
  33. package/src/scripts/nexo-learning-housekeep.py +157 -1
  34. package/src/scripts/nexo-learning-validator.py +19 -0
  35. package/src/scripts/nexo-postmortem-consolidator.py +3 -2
  36. package/src/scripts/nexo-sleep.py +16 -11
  37. package/src/scripts/nexo-synthesis.py +46 -3
  38. package/src/scripts/nexo-watchdog.sh +91 -30
  39. package/src/server.py +6 -1
  40. package/src/tools_coordination.py +1 -0
  41. package/src/tools_sessions.py +1 -0
  42. package/scripts/migrate-to-unified 2.sh +0 -813
  43. package/scripts/migrate-to-unified.sh +0 -813
  44. package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
  45. package/scripts/migrate-v1.5-to-v1.6.py +0 -778
  46. package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
  47. package/scripts/migrate-v1.7-to-v1.8.py +0 -214
  48. package/scripts/pre-commit-check 2.sh +0 -55
  49. package/scripts/pre-commit-check.sh +0 -55
  50. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  51. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  52. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  53. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  54. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  55. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  56. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  57. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  58. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  59. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  60. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  61. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  62. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  63. package/src/auto_close_sessions 2.py +0 -159
  64. package/src/auto_update 2.py +0 -634
  65. package/src/claim_graph 2.py +0 -323
  66. package/src/cognitive/__init__ 2.py +0 -62
  67. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  68. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  69. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  70. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  71. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  72. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  73. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  74. package/src/cognitive/_core 2.py +0 -567
  75. package/src/cognitive/_decay 2.py +0 -382
  76. package/src/cognitive/_ingest 2.py +0 -892
  77. package/src/cognitive/_memory 2.py +0 -912
  78. package/src/cognitive/_search 2.py +0 -949
  79. package/src/cognitive/_trust 2.py +0 -464
  80. package/src/crons/manifest 2.json +0 -106
  81. package/src/crons/sync 2.py +0 -217
  82. package/src/dashboard/__init__ 2.py +0 -0
  83. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  84. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  85. package/src/dashboard/app 2.py +0 -789
  86. package/src/db/__init__ 2.py +0 -89
  87. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  88. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  89. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  90. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  91. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  92. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  93. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  94. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  95. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  96. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  97. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  98. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  99. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  100. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  101. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  102. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  103. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  104. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  105. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  106. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  107. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  108. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  109. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  110. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  111. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  112. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  113. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  114. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  115. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  116. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  117. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  118. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  119. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  120. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  121. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  122. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  123. package/src/db/_core 2.py +0 -417
  124. package/src/db/_credentials 2.py +0 -124
  125. package/src/db/_entities 2.py +0 -178
  126. package/src/db/_episodic 2.py +0 -738
  127. package/src/db/_evolution 2.py +0 -54
  128. package/src/db/_fts 2.py +0 -406
  129. package/src/db/_learnings 2.py +0 -168
  130. package/src/db/_reminders 2.py +0 -338
  131. package/src/db/_schema 2.py +0 -364
  132. package/src/db/_sessions 2.py +0 -300
  133. package/src/db/_tasks 2.py +0 -91
  134. package/src/evolution_cycle 2.py +0 -266
  135. package/src/hnsw_index 2.py +0 -254
  136. package/src/hooks/auto_capture 2.py +0 -208
  137. package/src/hooks/caffeinate-guard 2.sh +0 -8
  138. package/src/hooks/capture-session 2.sh +0 -21
  139. package/src/hooks/capture-tool-logs 2.sh +0 -127
  140. package/src/hooks/daily-briefing-check 2.sh +0 -33
  141. package/src/hooks/inbox-hook 2.sh +0 -76
  142. package/src/hooks/post-compact 2.sh +0 -148
  143. package/src/hooks/pre-compact 2.sh +0 -151
  144. package/src/hooks/session-start 2.sh +0 -268
  145. package/src/hooks/session-stop 2.sh +0 -140
  146. package/src/kg_populate 2.py +0 -290
  147. package/src/knowledge_graph 2.py +0 -257
  148. package/src/maintenance 2.py +0 -59
  149. package/src/migrate_embeddings 2.py +0 -122
  150. package/src/plugin_loader 2.py +0 -202
  151. package/src/plugins/__init__ 2.py +0 -0
  152. package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
  153. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  154. package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
  155. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  156. package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
  157. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  158. package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
  159. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  160. package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
  161. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  162. package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
  163. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  164. package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
  165. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  166. package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
  167. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  168. package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
  169. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  170. package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
  171. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  172. package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
  173. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  174. package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
  175. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  176. package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
  177. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  178. package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
  179. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  180. package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
  181. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  182. package/src/plugins/adaptive_mode 2.py +0 -805
  183. package/src/plugins/agents 2.py +0 -52
  184. package/src/plugins/artifact_registry 2.py +0 -450
  185. package/src/plugins/backup 2.py +0 -104
  186. package/src/plugins/cognitive_memory 2.py +0 -564
  187. package/src/plugins/core_rules 2.py +0 -252
  188. package/src/plugins/cortex 2.py +0 -299
  189. package/src/plugins/entities 2.py +0 -67
  190. package/src/plugins/episodic_memory 2.py +0 -533
  191. package/src/plugins/evolution 2.py +0 -115
  192. package/src/plugins/guard 2.py +0 -746
  193. package/src/plugins/knowledge_graph_tools 2.py +0 -105
  194. package/src/plugins/preferences 2.py +0 -47
  195. package/src/plugins/update 2.py +0 -256
  196. package/src/requirements 2.txt +0 -12
  197. package/src/rules/__init__ 2.py +0 -0
  198. package/src/rules/core-rules 2.json +0 -331
  199. package/src/rules/migrate 2.py +0 -207
  200. package/src/scripts/check-context 2.py +0 -264
  201. package/src/scripts/nexo-auto-update 2.py +0 -6
  202. package/src/scripts/nexo-backup 2.sh +0 -25
  203. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  204. package/src/scripts/nexo-catchup 2.py +0 -242
  205. package/src/scripts/nexo-cognitive-decay 2.py +0 -182
  206. package/src/scripts/nexo-daily-self-audit 2.py +0 -552
  207. package/src/scripts/nexo-deep-sleep 2.sh +0 -97
  208. package/src/scripts/nexo-evolution-run 2.py +0 -597
  209. package/src/scripts/nexo-followup-hygiene 2.py +0 -112
  210. package/src/scripts/nexo-github-monitor 2.py +0 -256
  211. package/src/scripts/nexo-github-monitor.py +0 -256
  212. package/src/scripts/nexo-immune 2.py +0 -927
  213. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  214. package/src/scripts/nexo-install 2.py +0 -6
  215. package/src/scripts/nexo-learning-housekeep 2.py +0 -245
  216. package/src/scripts/nexo-learning-validator 2.py +0 -207
  217. package/src/scripts/nexo-migrate 2.py +0 -232
  218. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
  219. package/src/scripts/nexo-pre-commit 2.py +0 -120
  220. package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
  221. package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
  222. package/src/scripts/nexo-reflection 2.py +0 -253
  223. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  224. package/src/scripts/nexo-send-email 2.py +0 -25
  225. package/src/scripts/nexo-send-email.py +0 -25
  226. package/src/scripts/nexo-send-reply 2.py +0 -178
  227. package/src/scripts/nexo-send-reply.py +0 -178
  228. package/src/scripts/nexo-sleep 2.py +0 -592
  229. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  230. package/src/scripts/nexo-synthesis 2.py +0 -253
  231. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  232. package/src/scripts/nexo-update 2.sh +0 -161
  233. package/src/scripts/nexo-watchdog 2.sh +0 -878
  234. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  235. package/src/server 2.py +0 -733
  236. package/src/storage_router 2.py +0 -32
  237. package/src/tools_coordination 2.py +0 -102
  238. package/src/tools_credentials 2.py +0 -68
  239. package/src/tools_learnings 2.py +0 -220
  240. package/src/tools_menu 2.py +0 -227
  241. package/src/tools_reminders 2.py +0 -86
  242. package/src/tools_reminders_crud 2.py +0 -159
  243. package/src/tools_sessions 2.py +0 -476
  244. package/src/tools_task_history 2.py +0 -57
  245. package/templates/CLAUDE.md 2.template +0 -63
  246. package/templates/openclaw 2.json +0 -13
  247. package/tests/__init__ 2.py +0 -0
  248. package/tests/__init__.py +0 -0
  249. package/tests/conftest 2.py +0 -71
  250. package/tests/conftest.py +0 -71
  251. package/tests/test_cognitive 2.py +0 -205
  252. package/tests/test_cognitive.py +0 -205
  253. package/tests/test_knowledge_graph 2.py +0 -140
  254. package/tests/test_knowledge_graph.py +0 -140
  255. package/tests/test_migrations 2.py +0 -137
  256. package/tests/test_migrations.py +0 -137
package/src/server 2.py DELETED
@@ -1,733 +0,0 @@
1
- """NEXO MCP Server — Phase 4: Hot-Reload Plugin System."""
2
-
3
- import os
4
- import signal
5
- import sys
6
-
7
- from fastmcp import FastMCP
8
- from db import init_db, rebuild_fts_index, get_db, close_db, fts_add_dir, fts_remove_dir, fts_list_dirs
9
- from tools_sessions import handle_startup, handle_heartbeat, handle_status, handle_context_packet, handle_smart_startup_query
10
- from tools_coordination import (
11
- handle_track, handle_untrack, handle_files,
12
- handle_send, handle_ask, handle_answer, handle_check_answer,
13
- )
14
- from tools_reminders import handle_reminders
15
- from tools_menu import handle_menu
16
- from tools_reminders_crud import (
17
- handle_reminder_create, handle_reminder_update,
18
- handle_reminder_complete, handle_reminder_delete,
19
- handle_followup_create, handle_followup_update,
20
- handle_followup_complete, handle_followup_delete,
21
- )
22
- from tools_learnings import (
23
- handle_learning_add, handle_learning_search,
24
- handle_learning_update, handle_learning_delete, handle_learning_list,
25
- )
26
- from tools_credentials import (
27
- handle_credential_get, handle_credential_create,
28
- handle_credential_update, handle_credential_delete, handle_credential_list,
29
- )
30
- from tools_task_history import (
31
- handle_task_log, handle_task_list, handle_task_frequency,
32
- )
33
- from plugin_loader import load_all_plugins, load_plugin, remove_plugin, list_plugins
34
-
35
-
36
- # ── Graceful shutdown: close DB on any termination signal ──────────
37
- def _shutdown_handler(signum, frame):
38
- close_db()
39
- sys.exit(0)
40
-
41
-
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)
87
-
88
-
89
- mcp = FastMCP(
90
- name="nexo",
91
- instructions=(
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`"
108
- ),
109
- )
110
-
111
-
112
- # ── Session management (3 tools) ──────────────────────────────────
113
-
114
- @mcp.tool
115
- def nexo_startup(task: str = "Startup", claude_session_id: str = "") -> str:
116
- """Register new session, clean stale ones, return active sessions + alerts.
117
-
118
- Call this ONCE at the start of every conversation.
119
- Returns the session ID (SID) — store it for use in all other nexo_ tools.
120
-
121
- Args:
122
- task: Initial task description.
123
- claude_session_id: The Claude Code session UUID (from session-briefing or hook).
124
- Pass this to enable automatic inter-terminal inbox detection.
125
- """
126
- return handle_startup(task, claude_session_id=claude_session_id)
127
-
128
-
129
- @mcp.tool
130
- def nexo_heartbeat(sid: str, task: str, context_hint: str = '') -> str:
131
- """Update session task, check inbox and pending questions. Auto-detects trust events.
132
-
133
- Call this at the START of every user interaction (before doing work).
134
- Args:
135
- sid: Your session ID from nexo_startup.
136
- task: Brief description of current work (5-10 words).
137
- context_hint: Last 2-3 sentences from the user or current topic. Used for sentiment detection, trust auto-scoring, and mid-session RAG. ALWAYS provide this for best results.
138
- """
139
- return handle_heartbeat(sid, task, context_hint)
140
-
141
-
142
- @mcp.tool
143
- def nexo_stop(sid: str) -> str:
144
- """Cleanly close a session. Removes it from active sessions immediately.
145
-
146
- Call this when ending a conversation to avoid ghost sessions.
147
- Args:
148
- sid: Session ID to close."""
149
- from tools_sessions import handle_stop
150
- return handle_stop(sid)
151
-
152
- @mcp.tool
153
- def nexo_status(keyword: str = "") -> str:
154
- """List active sessions. Filter by keyword if provided."""
155
- return handle_status(keyword if keyword else None)
156
-
157
-
158
- @mcp.tool
159
- def nexo_context_packet(area: str, files: str = "") -> str:
160
- """Build a context packet for subagent injection. Returns learnings + changes + followups + preferences + cognitive memories for a specific area.
161
-
162
- MUST call before delegating ANY task to a subagent. Inject the result into the subagent's prompt.
163
-
164
- Args:
165
- area: Project/area name (e.g., 'ecommerce', 'shopify', 'backend', 'mobile-app', 'nexo', 'infrastructure').
166
- files: Optional comma-separated file paths for additional context.
167
- """
168
- return handle_context_packet(area, files)
169
-
170
-
171
- @mcp.tool
172
- def nexo_smart_startup() -> str:
173
- """Pre-load relevant cognitive memories based on pending followups, due reminders, and last session topics.
174
-
175
- Call during startup (after nexo_startup) to ensure the session starts with the right context loaded.
176
- Returns up to 10 memories matching the current operational state.
177
- """
178
- return handle_smart_startup_query()
179
-
180
-
181
- # ── Session Checkpoints (auto-compaction continuity) ──────────────
182
-
183
- @mcp.tool
184
- def nexo_checkpoint_save(
185
- sid: str,
186
- task: str = '',
187
- task_status: str = 'active',
188
- active_files: str = '[]',
189
- current_goal: str = '',
190
- decisions_summary: str = '',
191
- errors_found: str = '',
192
- reasoning_thread: str = '',
193
- next_step: str = ''
194
- ) -> str:
195
- """Save a session checkpoint for auto-compaction continuity.
196
-
197
- Call this BEFORE context compaction to preserve session state.
198
- The PostCompact hook reads this checkpoint and re-injects it as a
199
- Core Memory Block, so the session continues seamlessly.
200
-
201
- Args:
202
- sid: Session ID.
203
- task: Current task description.
204
- task_status: One of 'active', 'investigating', 'fixing', 'deploying', 'blocked'.
205
- active_files: JSON array of file paths currently being worked on.
206
- current_goal: What you're trying to achieve right now (1-2 sentences).
207
- decisions_summary: Recent decisions with brief reasoning (2-3 lines).
208
- errors_found: Errors encountered and their status (resolved/open).
209
- reasoning_thread: Your current chain of thought (1-2 sentences).
210
- next_step: The concrete next action to take.
211
- """
212
- from db import save_checkpoint
213
- result = save_checkpoint(
214
- sid=sid, task=task, task_status=task_status,
215
- active_files=active_files, current_goal=current_goal,
216
- decisions_summary=decisions_summary, errors_found=errors_found,
217
- reasoning_thread=reasoning_thread, next_step=next_step,
218
- )
219
- return f"Checkpoint saved for {sid}. Compaction #{result['compaction_count']}. PostCompact will re-inject this as Core Memory Block."
220
-
221
-
222
- @mcp.tool
223
- def nexo_checkpoint_read(sid: str = '') -> str:
224
- """Read the latest session checkpoint. Used by PostCompact hook and for manual recovery.
225
-
226
- Args:
227
- sid: Session ID. If empty, returns the most recent checkpoint from any session.
228
- """
229
- from db import read_checkpoint
230
- cp = read_checkpoint(sid)
231
- if not cp:
232
- return "No checkpoint found."
233
-
234
- lines = [f"CHECKPOINT for {cp['sid']} (compaction #{cp['compaction_count']})"]
235
- lines.append(f"Task: {cp['task']} ({cp['task_status']})")
236
- if cp.get('current_goal'):
237
- lines.append(f"Goal: {cp['current_goal']}")
238
- if cp.get('active_files') and cp['active_files'] != '[]':
239
- lines.append(f"Files: {cp['active_files']}")
240
- if cp.get('decisions_summary'):
241
- lines.append(f"Decisions: {cp['decisions_summary']}")
242
- if cp.get('errors_found'):
243
- lines.append(f"Errors: {cp['errors_found']}")
244
- if cp.get('reasoning_thread'):
245
- lines.append(f"Context: {cp['reasoning_thread']}")
246
- if cp.get('next_step'):
247
- lines.append(f"Next: {cp['next_step']}")
248
- lines.append(f"Updated: {cp['updated_at']}")
249
- return "\n".join(lines)
250
-
251
-
252
- # ── File coordination (3 tools) ───────────────────────────────────
253
-
254
- @mcp.tool
255
- def nexo_track(sid: str, paths: list[str]) -> str:
256
- """Track files being edited. Detects conflicts with other sessions.
257
-
258
- MUST call before editing any shared file.
259
- Args:
260
- sid: Your session ID.
261
- paths: List of absolute file paths to track.
262
- """
263
- return handle_track(sid, paths)
264
-
265
-
266
- @mcp.tool
267
- def nexo_untrack(sid: str, paths: list[str] | None = None) -> str:
268
- """Stop tracking files. If no paths given, releases all.
269
-
270
- Args:
271
- sid: Your session ID.
272
- paths: File paths to release. Omit to release all.
273
- """
274
- return handle_untrack(sid, paths)
275
-
276
-
277
- @mcp.tool
278
- def nexo_files() -> str:
279
- """Show all tracked files across all active sessions with conflict detection."""
280
- return handle_files()
281
-
282
-
283
- # ── Messaging (4 tools) ───────────────────────────────────────────
284
-
285
- @mcp.tool
286
- def nexo_send(from_sid: str, to_sid: str, text: str) -> str:
287
- """Send a fire-and-forget message to another session or broadcast.
288
-
289
- Args:
290
- from_sid: Your session ID.
291
- to_sid: Target session ID, or 'all' for broadcast.
292
- text: Message content.
293
- """
294
- return handle_send(from_sid, to_sid, text)
295
-
296
-
297
- @mcp.tool
298
- def nexo_ask(from_sid: str, to_sid: str, question: str) -> str:
299
- """Ask a question to another session (they see it on next heartbeat).
300
-
301
- Args:
302
- from_sid: Your session ID.
303
- to_sid: Target session ID.
304
- question: The question text.
305
- Returns: Question ID (qid) for checking the answer later.
306
- """
307
- return handle_ask(from_sid, to_sid, question)
308
-
309
-
310
- @mcp.tool
311
- def nexo_answer(qid: str, answer: str) -> str:
312
- """Answer a pending question from another session.
313
-
314
- Args:
315
- qid: The question ID shown in heartbeat output.
316
- answer: Your response.
317
- """
318
- return handle_answer(qid, answer)
319
-
320
-
321
- @mcp.tool
322
- def nexo_check_answer(qid: str) -> str:
323
- """Check if a question has been answered.
324
-
325
- Args:
326
- qid: The question ID from nexo_ask.
327
- """
328
- return handle_check_answer(qid)
329
-
330
-
331
- # ── Operations: Reminders + Menu (2 tools, read-only) ─────────────
332
-
333
- @mcp.tool
334
- def nexo_reminders(filter: str = "due") -> str:
335
- """Check reminders and followups.
336
-
337
- Args:
338
- filter: 'due' (vencidos/hoy), 'all' (todos activos), 'followups' (solo NEXO followups)
339
- """
340
- return handle_reminders(filter)
341
-
342
-
343
- @mcp.tool
344
- def nexo_menu() -> str:
345
- """Generate the NEXO operations center menu with alerts and active sessions.
346
-
347
- Shows: date, due alerts, all menu items by category, active sessions.
348
- Uses box-drawing characters for formatting.
349
- """
350
- return handle_menu()
351
-
352
-
353
- # ── Reminders CRUD (4 tools) ──────────────────────────────────────
354
-
355
- @mcp.tool
356
- def nexo_reminder_create(id: str, description: str, date: str = "", category: str = "general") -> str:
357
- """Create a new reminder for the user.
358
-
359
- Args:
360
- id: Unique ID starting with 'R' (e.g., R90).
361
- description: What needs to be done.
362
- date: Target date YYYY-MM-DD (optional).
363
- category: One of: decisions, tasks, waiting, ideas, general.
364
- """
365
- return handle_reminder_create(id, description, date, category)
366
-
367
-
368
- @mcp.tool
369
- def nexo_reminder_update(id: str, description: str = "", date: str = "", status: str = "", category: str = "") -> str:
370
- """Update fields of an existing reminder. Only non-empty fields are changed.
371
-
372
- Args:
373
- id: Reminder ID (e.g., R87).
374
- description: New description (optional).
375
- date: New date YYYY-MM-DD (optional).
376
- status: New status (optional).
377
- category: New category (optional).
378
- """
379
- return handle_reminder_update(id, description, date, status, category)
380
-
381
-
382
- @mcp.tool
383
- def nexo_reminder_complete(id: str) -> str:
384
- """Mark a reminder as completed with today's date.
385
-
386
- Args:
387
- id: Reminder ID (e.g., R87).
388
- """
389
- return handle_reminder_complete(id)
390
-
391
-
392
- @mcp.tool
393
- def nexo_reminder_delete(id: str) -> str:
394
- """Delete a reminder permanently.
395
-
396
- Args:
397
- id: Reminder ID (e.g., R87).
398
- """
399
- return handle_reminder_delete(id)
400
-
401
-
402
- # ── Followups CRUD (4 tools) ──────────────────────────────────────
403
-
404
- @mcp.tool
405
- def nexo_followup_create(id: str, description: str, date: str = "", verification: str = "", reasoning: str = "", recurrence: str = "", priority: str = "medium") -> str:
406
- """Create a new NEXO followup (autonomous task).
407
-
408
- Args:
409
- id: Unique ID starting with 'NF' (e.g., NF-MCP2).
410
- description: What to verify/do.
411
- date: Target date YYYY-MM-DD (optional).
412
- verification: How to verify completion (optional).
413
- reasoning: WHY this followup exists — what decision/context led to it (optional).
414
- recurrence: Auto-regenerate pattern (optional). Formats: 'weekly:monday', 'monthly:1', 'monthly:15', 'quarterly'.
415
- When completed, a new followup is auto-created with the next date. The completed one is archived with date suffix.
416
- priority: critical, high, medium, low (default: medium).
417
- """
418
- result = handle_followup_create(id, description, date, verification, reasoning, recurrence)
419
- if priority in ('critical', 'high', 'low') and 'created' in result:
420
- from db import get_db
421
- get_db().execute("UPDATE followups SET priority = ? WHERE id = ?", (priority, id))
422
- get_db().commit()
423
- return result
424
-
425
-
426
- @mcp.tool
427
- def nexo_followup_update(id: str, description: str = "", date: str = "", verification: str = "", status: str = "", priority: str = "") -> str:
428
- """Update fields of an existing followup. Only non-empty fields are changed.
429
-
430
- Args:
431
- id: Followup ID (e.g., NF45).
432
- description: New description (optional).
433
- date: New date YYYY-MM-DD (optional).
434
- verification: New verification text (optional).
435
- status: New status (optional).
436
- priority: critical, high, medium, low (optional).
437
- """
438
- result = handle_followup_update(id, description, date, verification, status)
439
- if priority in ('critical', 'high', 'medium', 'low'):
440
- from db import get_db
441
- get_db().execute("UPDATE followups SET priority = ? WHERE id = ?", (priority, id))
442
- get_db().commit()
443
- return result
444
-
445
-
446
- @mcp.tool
447
- def nexo_followup_complete(id: str, result: str = "") -> str:
448
- """Mark a followup as completed. Appends result to verification field.
449
-
450
- Args:
451
- id: Followup ID (e.g., NF45).
452
- result: What was found/done (optional).
453
- """
454
- return handle_followup_complete(id, result)
455
-
456
-
457
- @mcp.tool
458
- def nexo_followup_delete(id: str) -> str:
459
- """Delete a followup permanently.
460
-
461
- Args:
462
- id: Followup ID (e.g., NF45).
463
- """
464
- return handle_followup_delete(id)
465
-
466
-
467
- # ── Learnings CRUD (5 tools) ──────────────────────────────────────
468
-
469
- @mcp.tool
470
- def nexo_learning_add(category: str, title: str, content: str, reasoning: str = "", priority: str = "medium") -> str:
471
- """Add a new learning (resolved error, pattern, gotcha).
472
-
473
- Args:
474
- category: Free-form category name (e.g., 'backend', 'frontend', 'devops', 'infrastructure', 'security'). Use consistent names across learnings.
475
- title: Short title for the learning.
476
- content: Full description with context and solution.
477
- reasoning: WHY this matters — what led to discovering this (optional).
478
- priority: critical, high, medium, low (default: medium). Critical/high never decay below floor.
479
- """
480
- return handle_learning_add(category, title, content, reasoning, priority=priority)
481
-
482
-
483
- @mcp.tool
484
- def nexo_learning_search(query: str, category: str = "") -> str:
485
- """Search learnings by keyword. Searches title and content.
486
-
487
- Args:
488
- query: Search term.
489
- category: Filter by category (optional).
490
- """
491
- return handle_learning_search(query, category)
492
-
493
-
494
- @mcp.tool
495
- def nexo_learning_update(id: int, title: str = "", content: str = "", category: str = "", priority: str = "") -> str:
496
- """Update a learning entry. Only non-empty fields are changed.
497
-
498
- Args:
499
- id: Learning ID number.
500
- title: New title (optional).
501
- content: New content (optional).
502
- category: New category (optional).
503
- priority: critical, high, medium, low (optional).
504
- """
505
- return handle_learning_update(id, title, content, category, priority=priority)
506
-
507
-
508
- @mcp.tool
509
- def nexo_learning_delete(id: int) -> str:
510
- """Delete a learning entry.
511
-
512
- Args:
513
- id: Learning ID number.
514
- """
515
- return handle_learning_delete(id)
516
-
517
-
518
- @mcp.tool
519
- def nexo_learning_list(category: str = "") -> str:
520
- """List all learnings, grouped by category.
521
-
522
- Args:
523
- category: Filter by category (optional). If empty, shows all grouped.
524
- """
525
- return handle_learning_list(category)
526
-
527
-
528
- # ── Search index ──────────────────────────────────────────────────
529
-
530
- @mcp.tool
531
- def nexo_reindex() -> str:
532
- """Force full rebuild of the FTS5 search index. Use after bulk changes or if search seems stale."""
533
- conn = get_db()
534
- rebuild_fts_index(conn)
535
- count = conn.execute("SELECT COUNT(*) FROM unified_search").fetchone()[0]
536
- sources = conn.execute("SELECT source, COUNT(*) as cnt FROM unified_search GROUP BY source ORDER BY cnt DESC").fetchall()
537
- lines = [f"Index rebuilt: {count} documentos"]
538
- for s in sources:
539
- lines.append(f" {s[0]:12s} → {s[1]}")
540
- return "\n".join(lines)
541
-
542
-
543
- @mcp.tool
544
- def nexo_index_add_dir(path: str, dir_type: str = "code",
545
- patterns: str = "*.php,*.js,*.json,*.py,*.ts,*.tsx",
546
- notes: str = "") -> str:
547
- """Register a new directory for FTS5 search indexing. Survives restarts.
548
-
549
- Args:
550
- path: Absolute path to directory (supports ~).
551
- dir_type: 'code' for source files, 'md' for markdown docs.
552
- patterns: Comma-separated glob patterns (only for code type).
553
- notes: Description of what this directory contains.
554
- """
555
- result = fts_add_dir(path, dir_type, patterns, notes)
556
- if "error" in result:
557
- return f"ERROR: {result['error']}"
558
- return f"Directory registered: {result['path']} ({result['dir_type']}, patterns: {result['patterns']})\nUse nexo_reindex to index now."
559
-
560
-
561
- @mcp.tool
562
- def nexo_index_remove_dir(path: str) -> str:
563
- """Remove a directory from FTS5 indexing and clean up its entries.
564
-
565
- Args:
566
- path: Path to directory to remove.
567
- """
568
- result = fts_remove_dir(path)
569
- if "error" in result:
570
- return f"ERROR: {result['error']}"
571
- return f"Directory removed from index: {result['removed']}"
572
-
573
-
574
- @mcp.tool
575
- def nexo_index_dirs() -> str:
576
- """List all directories being indexed by FTS5 (builtin + dynamic)."""
577
- dirs = fts_list_dirs()
578
- if not dirs:
579
- return "No directories configured."
580
- lines = ["INDEXED DIRECTORIES:"]
581
- for d in dirs:
582
- source_tag = "⚙️" if d["source"] == "builtin" else "➕"
583
- notes = f" — {d['notes']}" if d.get("notes") else ""
584
- lines.append(f" {source_tag} [{d['type']}] {d['path']}")
585
- lines.append(f" patterns: {d['patterns']}{notes}")
586
- return "\n".join(lines)
587
-
588
-
589
- # ── Credentials CRUD (5 tools) ────────────────────────────────────
590
-
591
- @mcp.tool
592
- def nexo_credential_get(service: str, key: str = "") -> str:
593
- """Get credential value(s) for a service.
594
-
595
- Args:
596
- service: Service name (e.g., google-ads, meta-ads, shopify).
597
- key: Specific key (optional). If empty, returns all for the service.
598
- """
599
- return handle_credential_get(service, key)
600
-
601
-
602
- @mcp.tool
603
- def nexo_credential_create(service: str, key: str, value: str, notes: str = "") -> str:
604
- """Store a new credential.
605
-
606
- Args:
607
- service: Service name (e.g., google-ads, cloudflare).
608
- key: Key name (e.g., api_key, token, ssh).
609
- value: The secret value.
610
- notes: Description or context (optional).
611
- """
612
- return handle_credential_create(service, key, value, notes)
613
-
614
-
615
- @mcp.tool
616
- def nexo_credential_update(service: str, key: str, value: str = "", notes: str = "") -> str:
617
- """Update a credential's value and/or notes.
618
-
619
- Args:
620
- service: Service name.
621
- key: Key name.
622
- value: New value (optional).
623
- notes: New notes (optional).
624
- """
625
- return handle_credential_update(service, key, value, notes)
626
-
627
-
628
- @mcp.tool
629
- def nexo_credential_delete(service: str, key: str = "") -> str:
630
- """Delete credential(s). If no key, deletes all for the service.
631
-
632
- Args:
633
- service: Service name.
634
- key: Specific key (optional). If empty, deletes ALL for service.
635
- """
636
- return handle_credential_delete(service, key)
637
-
638
-
639
- @mcp.tool
640
- def nexo_credential_list(service: str = "") -> str:
641
- """List credentials (names and notes only, no values).
642
-
643
- Args:
644
- service: Filter by service (optional). If empty, shows all.
645
- """
646
- return handle_credential_list(service)
647
-
648
-
649
- # ── Task History (3 tools) ────────────────────────────────────────
650
-
651
- @mcp.tool
652
- def nexo_task_log(task_num: str, task_name: str, notes: str = "", reasoning: str = "") -> str:
653
- """Record that an operational task was executed.
654
-
655
- Args:
656
- task_num: Task number from the checklist (e.g., '7', '7b').
657
- task_name: Task name (e.g., 'Google Ads').
658
- notes: Execution summary (optional).
659
- reasoning: WHY this task was executed now — what triggered it (optional).
660
- """
661
- return handle_task_log(task_num, task_name, notes, reasoning)
662
-
663
-
664
- @mcp.tool
665
- def nexo_task_list(task_num: str = "", days: int = 30) -> str:
666
- """Show execution history for operational tasks.
667
-
668
- Args:
669
- task_num: Filter by task number (optional).
670
- days: How many days back to show (default 30).
671
- """
672
- return handle_task_list(task_num, days)
673
-
674
-
675
- @mcp.tool
676
- def nexo_task_frequency() -> str:
677
- """Check which operational tasks are overdue based on their frequency.
678
-
679
- Compares last execution date vs configured frequency.
680
- Returns overdue tasks or 'all tasks up to date'.
681
- """
682
- return handle_task_frequency()
683
-
684
-
685
- # ── Plugin Management (3 tools) ─────────────────────────────────
686
-
687
- @mcp.tool
688
- def nexo_plugin_load(filename: str) -> str:
689
- """Load or reload a plugin. Searches repo plugins/ first, then NEXO_HOME/plugins/.
690
-
691
- Args:
692
- filename: Plugin filename (e.g., 'entities.py').
693
- """
694
- try:
695
- n = load_plugin(mcp, filename)
696
- return f"Plugin {filename}: {n} tools registered."
697
- except Exception as e:
698
- return f"Error loading plugin {filename}: {e}"
699
-
700
-
701
- @mcp.tool
702
- def nexo_plugin_list() -> str:
703
- """List all loaded plugins and their tools, showing source (repo/personal)."""
704
- plugins = list_plugins()
705
- if not plugins:
706
- return "No plugins loaded."
707
- lines = ["LOADED PLUGINS:"]
708
- for p in plugins:
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}")
712
- return "\n".join(lines)
713
-
714
-
715
- @mcp.tool
716
- def nexo_plugin_remove(filename: str) -> str:
717
- """Unregister a plugin's tools from MCP (does not delete files).
718
-
719
- Args:
720
- filename: Plugin filename (e.g., 'entities.py').
721
- """
722
- try:
723
- removed = remove_plugin(mcp, filename)
724
- if removed:
725
- return f"Plugin {filename} unregistered. Tools removed: {', '.join(removed)}"
726
- return f"Plugin {filename} unregistered (had no registered tools)."
727
- except Exception as e:
728
- return f"Error removing plugin {filename}: {e}"
729
-
730
-
731
- if __name__ == "__main__":
732
- _server_init()
733
- mcp.run(transport="stdio")