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
@@ -1,105 +0,0 @@
1
- """Knowledge Graph MCP tools — query, path, neighbors, stats."""
2
- import os
3
- import sys
4
-
5
- sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
6
- import knowledge_graph as kg
7
-
8
-
9
- def _find_node(node_type: str, node_ref: str):
10
- """Find a node, trying both raw ref and type-prefixed ref (area:X, file:X)."""
11
- node = kg.get_node(node_type, node_ref)
12
- if not node:
13
- node = kg.get_node(node_type, f"{node_type}:{node_ref}")
14
- return node
15
-
16
-
17
- def handle_kg_query(node_type: str, node_ref: str, depth: int = 2, relation: str = "") -> str:
18
- """Traverse the knowledge graph from a node up to `depth` hops."""
19
- node = _find_node(node_type, node_ref)
20
- if not node:
21
- return f"Node not found: {node_type}/{node_ref}"
22
- result = kg.traverse(node["id"], max_depth=depth, relation_filter=relation or None)
23
- nodes = result["nodes"][:30]
24
- edges = result["edges"][:30]
25
- lines = [f"KG TRAVERSE — {node['label']} ({node_type}/{node_ref}) depth={depth}"]
26
- lines.append(f"Nodes: {len(result['nodes'])} Edges: {len(result['edges'])}")
27
- lines.append("")
28
- lines.append("NODES:")
29
- for n in nodes:
30
- indent = " " * n.get("depth", 0)
31
- lines.append(f" {indent}[{n['id']}] ({n['node_type']}) {n['label']} — {n['node_ref']}")
32
- lines.append("")
33
- lines.append("EDGES:")
34
- for e in edges[:20]:
35
- lines.append(f" [{e['source_id']}] --{e['relation']}--> [{e['target_id']}] w={e['weight']}")
36
- return "\n".join(lines)
37
-
38
-
39
- def handle_kg_path(from_type: str, from_ref: str, to_type: str, to_ref: str) -> str:
40
- """Find the shortest path between two nodes in the knowledge graph."""
41
- from_node = _find_node(from_type, from_ref)
42
- if not from_node:
43
- return f"Source node not found: {from_type}/{from_ref}"
44
- to_node = _find_node(to_type, to_ref)
45
- if not to_node:
46
- return f"Target node not found: {to_type}/{to_ref}"
47
- path_ids = kg.shortest_path(from_node["id"], to_node["id"])
48
- if not path_ids:
49
- return f"No path found between {from_ref} and {to_ref}"
50
- lines = [f"PATH ({len(path_ids) - 1} hops): {from_ref} → {to_ref}"]
51
- for i, nid in enumerate(path_ids):
52
- node = kg.get_node_by_id(nid)
53
- label = node["label"] if node else f"[{nid}]"
54
- ntype = node["node_type"] if node else "?"
55
- lines.append(f" {i}. [{nid}] ({ntype}) {label}")
56
- return "\n".join(lines)
57
-
58
-
59
- def handle_kg_neighbors(node_type: str, node_ref: str, relation: str = "") -> str:
60
- """Get direct neighbors of a node, optionally filtered by relation type."""
61
- node = _find_node(node_type, node_ref)
62
- if not node:
63
- return f"Node not found: {node_type}/{node_ref}"
64
- neighbors = kg.get_neighbors(node["id"], relation=relation or None)
65
- if not neighbors:
66
- rel_info = f" (relation={relation})" if relation else ""
67
- return f"No neighbors found for {node['label']}{rel_info}"
68
- lines = [f"NEIGHBORS of [{node['id']}] {node['label']} ({len(neighbors)} total):"]
69
- for n in neighbors[:30]:
70
- direction = n.get("direction", "?")
71
- arrow = "-->" if direction == "outgoing" else "<--"
72
- lines.append(f" {arrow} [{n['id']}] {n['label']} ({n['node_type']}) rel={n['relation']} w={n['weight']}")
73
- if len(neighbors) > 30:
74
- lines.append(f" ... +{len(neighbors) - 30} more")
75
- return "\n".join(lines)
76
-
77
-
78
- def handle_kg_stats() -> str:
79
- """Return knowledge graph statistics: node counts, edge counts, top connected nodes."""
80
- s = kg.stats()
81
- lines = ["KNOWLEDGE GRAPH STATS"]
82
- lines.append(f" Nodes: {s['nodes']}")
83
- lines.append(f" Edges (active): {s['edges_active']}")
84
- lines.append(f" Edges (historical): {s['edges_historical']}")
85
- if s["node_types"]:
86
- lines.append("\nNODE TYPES:")
87
- for t, cnt in sorted(s["node_types"].items(), key=lambda x: -x[1]):
88
- lines.append(f" {t}: {cnt}")
89
- if s["relation_types"]:
90
- lines.append("\nRELATION TYPES:")
91
- for r, cnt in sorted(s["relation_types"].items(), key=lambda x: -x[1])[:20]:
92
- lines.append(f" {r}: {cnt}")
93
- if s["most_connected"]:
94
- lines.append("\nMOST CONNECTED:")
95
- for n in s["most_connected"][:10]:
96
- lines.append(f" [{n['id']}] {n['label']} ({n['node_type']}) — {n['connections']} connections")
97
- return "\n".join(lines)
98
-
99
-
100
- TOOLS = [
101
- (handle_kg_query, "nexo_kg_query", "Query knowledge graph — traverse from a node"),
102
- (handle_kg_path, "nexo_kg_path", "Find shortest path between two nodes"),
103
- (handle_kg_neighbors, "nexo_kg_neighbors", "Get direct neighbors of a node"),
104
- (handle_kg_stats, "nexo_kg_stats", "Knowledge graph statistics"),
105
- ]
@@ -1,47 +0,0 @@
1
- """Preferences plugin — learned behavior patterns and workflow rules."""
2
- from db import set_preference, get_preference, list_preferences, delete_preference
3
-
4
- def handle_preference_get(key: str) -> str:
5
- """Get a specific preference by key."""
6
- p = get_preference(key)
7
- if not p: return f"Preference '{key}' not found."
8
- return f"{p['key']} = {p['value']} (cat: {p['category']})"
9
-
10
- def handle_preference_set(key: str, value: str, category: str = "general") -> str:
11
- """Set a preference (creates or updates)."""
12
- set_preference(key, value, category)
13
- try:
14
- import cognitive
15
- cognitive.ingest_to_ltm(f"{key}: {value}", "preference", key, key, "")
16
- except Exception:
17
- pass
18
- return f"Preference '{key}' = '{value}' ({category})"
19
-
20
- def handle_preference_list(category: str = "") -> str:
21
- """List all preferences, optionally filtered by category."""
22
- prefs = list_preferences(category)
23
- if not prefs: return "No preferences."
24
- grouped = {}
25
- for p in prefs:
26
- c = p["category"]
27
- if c not in grouped: grouped[c] = []
28
- grouped[c].append(p)
29
- lines = ["PREFERENCES:"]
30
- for c, items in grouped.items():
31
- lines.append(f"\n [{c.upper()}]")
32
- for p in items:
33
- lines.append(f" {p['key']} = {p['value']}")
34
- return "\n".join(lines)
35
-
36
- def handle_preference_delete(key: str) -> str:
37
- """Delete a preference."""
38
- if not delete_preference(key):
39
- return f"ERROR: Preference '{key}' not found."
40
- return f"Preference '{key}' deleted."
41
-
42
- TOOLS = [
43
- (handle_preference_get, "nexo_preference_get", "Get a specific preference value"),
44
- (handle_preference_set, "nexo_preference_set", "Set a preference (creates or updates)"),
45
- (handle_preference_list, "nexo_preference_list", "List all preferences grouped by category"),
46
- (handle_preference_delete, "nexo_preference_delete", "Delete a preference"),
47
- ]
@@ -1,256 +0,0 @@
1
- """Update plugin — pull latest code, backup DBs, run migrations, verify."""
2
- import json
3
- import os
4
- import shutil
5
- import sqlite3
6
- import subprocess
7
- import sys
8
- import time
9
- from pathlib import Path
10
-
11
- # Repo root: go up from src/plugins/ -> src/ -> repo/
12
- _THIS_DIR = Path(__file__).resolve().parent
13
- REPO_DIR = _THIS_DIR.parent.parent
14
- PACKAGE_JSON = REPO_DIR / "package.json"
15
- SRC_DIR = REPO_DIR / "src"
16
-
17
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
18
- DATA_DIR = NEXO_HOME / "data"
19
- BACKUP_BASE = NEXO_HOME / "backups"
20
-
21
-
22
- def _read_version() -> str:
23
- """Read version from package.json."""
24
- try:
25
- return json.loads(PACKAGE_JSON.read_text()).get("version", "unknown")
26
- except Exception:
27
- return "unknown"
28
-
29
-
30
- def _git(*args, cwd=None) -> tuple[int, str, str]:
31
- """Run a git command and return (returncode, stdout, stderr)."""
32
- result = subprocess.run(
33
- ["git"] + list(args),
34
- cwd=cwd or str(REPO_DIR),
35
- capture_output=True,
36
- text=True,
37
- timeout=60,
38
- )
39
- return result.returncode, result.stdout.strip(), result.stderr.strip()
40
-
41
-
42
- def _check_dirty() -> str | None:
43
- """Return error message if src/ has uncommitted changes, else None."""
44
- rc, out, _ = _git("status", "--porcelain", "--", "src/")
45
- if rc != 0:
46
- return "Failed to check git status."
47
- if out:
48
- return f"Uncommitted changes in src/:\n{out}\nCommit or stash before updating."
49
- return None
50
-
51
-
52
- def _backup_databases() -> tuple[str, str | None]:
53
- """Backup all .db files from NEXO_HOME/data/. Returns (backup_dir, error)."""
54
- timestamp = time.strftime("%Y-%m-%d-%H%M")
55
- backup_dir = BACKUP_BASE / f"pre-update-{timestamp}"
56
-
57
- db_files = list(DATA_DIR.glob("*.db")) if DATA_DIR.is_dir() else []
58
- # Also check NEXO_HOME root for legacy db location
59
- db_files += [f for f in NEXO_HOME.glob("*.db") if f.is_file()]
60
- # And check src/ dir for nexo.db (dev mode)
61
- src_db = SRC_DIR / "nexo.db"
62
- if src_db.is_file() and src_db not in db_files:
63
- db_files.append(src_db)
64
-
65
- if not db_files:
66
- return str(backup_dir), None # No DBs to backup, not an error
67
-
68
- backup_dir.mkdir(parents=True, exist_ok=True)
69
-
70
- for db_file in db_files:
71
- dest = backup_dir / db_file.name
72
- try:
73
- src_conn = sqlite3.connect(str(db_file))
74
- dst_conn = sqlite3.connect(str(dest))
75
- src_conn.backup(dst_conn)
76
- dst_conn.close()
77
- src_conn.close()
78
- except Exception as e:
79
- return str(backup_dir), f"Failed to backup {db_file.name}: {e}"
80
-
81
- return str(backup_dir), None
82
-
83
-
84
- def _restore_databases(backup_dir: str):
85
- """Restore .db files from a backup directory."""
86
- bdir = Path(backup_dir)
87
- if not bdir.is_dir():
88
- return
89
- for db_backup in bdir.glob("*.db"):
90
- # Try to find original location
91
- for candidate in [DATA_DIR / db_backup.name, NEXO_HOME / db_backup.name, SRC_DIR / db_backup.name]:
92
- if candidate.is_file():
93
- try:
94
- src_conn = sqlite3.connect(str(db_backup))
95
- dst_conn = sqlite3.connect(str(candidate))
96
- src_conn.backup(dst_conn)
97
- dst_conn.close()
98
- src_conn.close()
99
- except Exception:
100
- pass
101
- break
102
-
103
-
104
- def _run_migrations() -> str | None:
105
- """Run init_db() to apply pending migrations. Returns error or None."""
106
- try:
107
- result = subprocess.run(
108
- [sys.executable, "-c", "import db; db.init_db()"],
109
- cwd=str(SRC_DIR),
110
- capture_output=True,
111
- text=True,
112
- timeout=30,
113
- )
114
- if result.returncode != 0:
115
- return f"Migration failed: {result.stderr or result.stdout}"
116
- except Exception as e:
117
- return f"Migration error: {e}"
118
- return None
119
-
120
-
121
- def _verify_import() -> str | None:
122
- """Verify server.py can be imported successfully."""
123
- try:
124
- result = subprocess.run(
125
- [sys.executable, "-c", "import server"],
126
- cwd=str(SRC_DIR),
127
- capture_output=True,
128
- text=True,
129
- timeout=15,
130
- )
131
- if result.returncode != 0:
132
- return f"Import verification failed: {result.stderr or result.stdout}"
133
- except Exception as e:
134
- return f"Import verification error: {e}"
135
- return None
136
-
137
-
138
- def handle_update(remote: str = "origin", branch: str = "main") -> str:
139
- """Pull latest NEXO code, backup databases, run migrations, and verify.
140
-
141
- Full update flow:
142
- 1. Check for uncommitted changes in src/
143
- 2. Backup all .db files
144
- 3. git pull
145
- 4. Run migrations if version changed
146
- 5. Verify server.py imports
147
- 6. Rollback on failure
148
-
149
- Args:
150
- remote: Git remote name (default: origin)
151
- branch: Git branch to pull (default: main)
152
- """
153
- steps_done = []
154
- old_commit = None
155
- backup_dir = None
156
-
157
- try:
158
- # Step 1: Check dirty
159
- dirty_err = _check_dirty()
160
- if dirty_err:
161
- return f"ABORTED: {dirty_err}"
162
- steps_done.append("clean-check")
163
-
164
- # Record current state
165
- old_version = _read_version()
166
- rc, old_commit, _ = _git("rev-parse", "HEAD")
167
- if rc != 0:
168
- return "ABORTED: Not a git repository or git not available."
169
-
170
- # Step 2: Backup databases
171
- backup_dir, backup_err = _backup_databases()
172
- if backup_err:
173
- return f"ABORTED at backup: {backup_err}"
174
- steps_done.append("backup")
175
-
176
- # Step 3: git pull
177
- rc, pull_out, pull_err = _git("pull", remote, branch)
178
- if rc != 0:
179
- return f"ABORTED at git pull: {pull_err or pull_out}"
180
- steps_done.append("git-pull")
181
-
182
- # Step 4: Check version change
183
- new_version = _read_version()
184
- version_changed = old_version != new_version
185
-
186
- # Step 5: Run migrations if version changed
187
- if version_changed:
188
- mig_err = _run_migrations()
189
- if mig_err:
190
- raise RuntimeError(f"Migration failed: {mig_err}")
191
- steps_done.append("migrations")
192
-
193
- # Step 6: Verify import
194
- verify_err = _verify_import()
195
- if verify_err:
196
- raise RuntimeError(f"Verification failed: {verify_err}")
197
- steps_done.append("verify")
198
-
199
- # Step 7: Sync crons with manifest
200
- cron_sync_result = ""
201
- try:
202
- cron_sync_path = NEXO_CODE / "crons" / "sync.py"
203
- if cron_sync_path.exists():
204
- import subprocess as _sp
205
- r = _sp.run(
206
- [sys.executable, str(cron_sync_path)],
207
- capture_output=True, text=True, timeout=30,
208
- env={**os.environ, "NEXO_HOME": str(NEXO_HOME), "NEXO_CODE": str(NEXO_CODE)},
209
- )
210
- cron_sync_result = r.stdout.strip()
211
- steps_done.append("cron-sync")
212
- except Exception as e:
213
- cron_sync_result = f"Cron sync warning: {e}"
214
-
215
- # Build result
216
- if pull_out == "Already up to date.":
217
- return f"Already up to date (v{old_version}). No changes pulled."
218
-
219
- lines = ["UPDATE SUCCESSFUL"]
220
- if version_changed:
221
- lines.append(f" Version: {old_version} -> {new_version}")
222
- else:
223
- lines.append(f" Version: {old_version} (unchanged)")
224
- lines.append(f" Branch: {remote}/{branch}")
225
- lines.append(f" Backup: {backup_dir}")
226
- if version_changed:
227
- lines.append(" Migrations: applied")
228
- if "cron-sync" in steps_done:
229
- lines.append(" Crons: synced with manifest")
230
- lines.append("")
231
- lines.append("MCP server restart needed to load new code.")
232
- return "\n".join(lines)
233
-
234
- except Exception as e:
235
- # Rollback
236
- rollback_lines = [f"UPDATE FAILED: {e}", "", "Rolling back..."]
237
-
238
- if old_commit and "git-pull" in steps_done:
239
- rc, _, err = _git("reset", "--hard", old_commit)
240
- if rc == 0:
241
- rollback_lines.append(f" Git: reset to {old_commit[:8]}")
242
- else:
243
- rollback_lines.append(f" Git rollback FAILED: {err}")
244
-
245
- if backup_dir and "backup" in steps_done:
246
- _restore_databases(backup_dir)
247
- rollback_lines.append(f" DBs: restored from {backup_dir}")
248
-
249
- rollback_lines.append("")
250
- rollback_lines.append("System restored to previous state.")
251
- return "\n".join(rollback_lines)
252
-
253
-
254
- TOOLS = [
255
- (handle_update, "nexo_update", "Pull latest NEXO code, backup DBs, run migrations, verify. Rolls back on failure."),
256
- ]
@@ -1,12 +0,0 @@
1
- # NEXO Brain — runtime dependencies
2
- # Core (required)
3
- fastmcp>=2.9.0
4
- numpy
5
-
6
- # Embedding model (optional but recommended for cognitive features)
7
- fastembed
8
-
9
- # Dashboard (optional, only needed for `python -m dashboard.app`)
10
- fastapi
11
- uvicorn
12
- pydantic
File without changes