nexo-brain 2.3.0 → 2.3.2

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 (299) hide show
  1. package/README.md +1 -1
  2. package/bin/nexo-brain.js +92 -9
  3. package/bin/postinstall.js +22 -15
  4. package/package.json +7 -4
  5. package/src/auto_update.py +194 -5
  6. package/src/crons/sync.py +6 -2
  7. package/src/db/_core.py +1 -0
  8. package/src/db/_entities.py +1 -0
  9. package/src/db/_episodic.py +1 -0
  10. package/src/db/_learnings.py +1 -0
  11. package/src/db/_reminders.py +1 -0
  12. package/src/db/_schema.py +11 -1
  13. package/src/db/_sessions.py +1 -0
  14. package/src/db/_skills.py +1 -0
  15. package/src/hooks/capture-tool-logs.sh +23 -6
  16. package/src/hooks/session-start.sh +4 -3
  17. package/src/plugin_loader.py +1 -0
  18. package/src/plugins/update.py +377 -26
  19. package/src/scripts/deep-sleep/apply_findings.py +1 -0
  20. package/src/scripts/deep-sleep/collect.py +1 -0
  21. package/src/scripts/deep-sleep/extract.py +1 -0
  22. package/src/scripts/deep-sleep/synthesize.py +1 -0
  23. package/src/scripts/nexo-catchup.py +29 -4
  24. package/src/scripts/nexo-daily-self-audit.py +21 -1
  25. package/src/scripts/nexo-evolution-run.py +21 -1
  26. package/src/scripts/nexo-learning-housekeep.py +1 -0
  27. package/src/scripts/nexo-postmortem-consolidator.py +34 -9
  28. package/src/scripts/nexo-sleep.py +32 -10
  29. package/src/scripts/nexo-synthesis.py +29 -9
  30. package/src/scripts/nexo-update.sh +109 -7
  31. package/src/scripts/nexo-watchdog.sh +122 -58
  32. package/src/server.py +66 -1
  33. package/src/tools_coordination.py +1 -0
  34. package/src/tools_sessions.py +1 -0
  35. package/scripts/migrate-to-unified 2.sh +0 -813
  36. package/scripts/migrate-to-unified.sh +0 -813
  37. package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
  38. package/scripts/migrate-v1.5-to-v1.6.py +0 -778
  39. package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
  40. package/scripts/migrate-v1.7-to-v1.8.py +0 -214
  41. package/scripts/nexo-preflight.sh +0 -236
  42. package/scripts/pre-commit-check 2.sh +0 -55
  43. package/scripts/pre-commit-check.sh +0 -55
  44. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  45. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  46. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  47. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  48. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  49. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  50. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  51. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  52. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  53. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  54. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  55. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  56. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  57. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  58. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  59. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  60. package/src/auto_close_sessions 2.py +0 -159
  61. package/src/auto_update 2.py +0 -634
  62. package/src/claim_graph 2.py +0 -323
  63. package/src/cognitive/__init__ 2.py +0 -62
  64. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  65. package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
  66. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  67. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  68. package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
  69. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  70. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  71. package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
  72. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  73. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  74. package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
  75. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  76. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  77. package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
  78. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  79. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  80. package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
  81. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  82. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  83. package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
  84. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  85. package/src/cognitive/_core 2.py +0 -567
  86. package/src/cognitive/_decay 2.py +0 -382
  87. package/src/cognitive/_ingest 2.py +0 -892
  88. package/src/cognitive/_memory 2.py +0 -912
  89. package/src/cognitive/_search 2.py +0 -949
  90. package/src/cognitive/_trust 2.py +0 -464
  91. package/src/crons/__pycache__/sync.cpython-314.pyc +0 -0
  92. package/src/crons/manifest 2.json +0 -106
  93. package/src/crons/sync 2.py +0 -217
  94. package/src/dashboard/__init__ 2.py +0 -0
  95. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  96. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  97. package/src/dashboard/app 2.py +0 -789
  98. package/src/db/__init__ 2.py +0 -89
  99. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  100. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  101. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  102. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  103. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  104. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  105. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  106. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  107. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  108. package/src/db/__pycache__/_cron_runs.cpython-310.pyc +0 -0
  109. package/src/db/__pycache__/_cron_runs.cpython-314.pyc +0 -0
  110. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  111. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  112. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  113. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  114. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  115. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  116. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  117. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  118. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  119. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  120. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  121. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  122. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  123. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  124. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  125. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  126. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  127. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  128. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  129. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  130. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  131. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  132. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  133. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  134. package/src/db/__pycache__/_skills.cpython-310.pyc +0 -0
  135. package/src/db/__pycache__/_skills.cpython-312.pyc +0 -0
  136. package/src/db/__pycache__/_skills.cpython-314.pyc +0 -0
  137. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  138. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  139. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  140. package/src/db/_core 2.py +0 -417
  141. package/src/db/_credentials 2.py +0 -124
  142. package/src/db/_entities 2.py +0 -178
  143. package/src/db/_episodic 2.py +0 -738
  144. package/src/db/_evolution 2.py +0 -54
  145. package/src/db/_fts 2.py +0 -406
  146. package/src/db/_learnings 2.py +0 -168
  147. package/src/db/_reminders 2.py +0 -338
  148. package/src/db/_schema 2.py +0 -364
  149. package/src/db/_sessions 2.py +0 -300
  150. package/src/db/_tasks 2.py +0 -91
  151. package/src/evolution_cycle 2.py +0 -266
  152. package/src/hnsw_index 2.py +0 -254
  153. package/src/hooks/auto_capture 2.py +0 -208
  154. package/src/hooks/caffeinate-guard 2.sh +0 -8
  155. package/src/hooks/capture-session 2.sh +0 -21
  156. package/src/hooks/capture-tool-logs 2.sh +0 -127
  157. package/src/hooks/daily-briefing-check 2.sh +0 -33
  158. package/src/hooks/inbox-hook 2.sh +0 -76
  159. package/src/hooks/post-compact 2.sh +0 -148
  160. package/src/hooks/pre-compact 2.sh +0 -151
  161. package/src/hooks/session-start 2.sh +0 -268
  162. package/src/hooks/session-stop 2.sh +0 -140
  163. package/src/kg_populate 2.py +0 -290
  164. package/src/knowledge_graph 2.py +0 -257
  165. package/src/maintenance 2.py +0 -59
  166. package/src/migrate_embeddings 2.py +0 -122
  167. package/src/plugin_loader 2.py +0 -202
  168. package/src/plugins/__init__ 2.py +0 -0
  169. package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
  170. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  171. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  172. package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
  173. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  174. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  175. package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
  176. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  177. package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
  178. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  179. package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
  180. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  181. package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
  182. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  183. package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
  184. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  185. package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
  186. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  187. package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
  188. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  189. package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
  190. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  191. package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
  192. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  193. package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
  194. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  195. package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
  196. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  197. package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
  198. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  199. package/src/plugins/__pycache__/schedule.cpython-310.pyc +0 -0
  200. package/src/plugins/__pycache__/schedule.cpython-314.pyc +0 -0
  201. package/src/plugins/__pycache__/skills.cpython-310.pyc +0 -0
  202. package/src/plugins/__pycache__/skills.cpython-314.pyc +0 -0
  203. package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
  204. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  205. package/src/plugins/adaptive_mode 2.py +0 -805
  206. package/src/plugins/agents 2.py +0 -52
  207. package/src/plugins/artifact_registry 2.py +0 -450
  208. package/src/plugins/backup 2.py +0 -104
  209. package/src/plugins/cognitive_memory 2.py +0 -564
  210. package/src/plugins/core_rules 2.py +0 -252
  211. package/src/plugins/cortex 2.py +0 -299
  212. package/src/plugins/entities 2.py +0 -67
  213. package/src/plugins/episodic_memory 2.py +0 -533
  214. package/src/plugins/evolution 2.py +0 -115
  215. package/src/plugins/guard 2.py +0 -746
  216. package/src/plugins/knowledge_graph_tools 2.py +0 -105
  217. package/src/plugins/preferences 2.py +0 -47
  218. package/src/plugins/update 2.py +0 -256
  219. package/src/requirements 2.txt +0 -12
  220. package/src/rules/__init__ 2.py +0 -0
  221. package/src/rules/core-rules 2.json +0 -331
  222. package/src/rules/migrate 2.py +0 -207
  223. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  224. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  225. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  226. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  227. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  228. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  229. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  230. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  231. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  232. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  233. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  234. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  235. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  236. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  237. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  238. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  239. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  240. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  241. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  242. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  243. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  244. package/src/scripts/check-context 2.py +0 -264
  245. package/src/scripts/nexo-auto-update 2.py +0 -6
  246. package/src/scripts/nexo-backup 2.sh +0 -25
  247. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  248. package/src/scripts/nexo-catchup 2.py +0 -242
  249. package/src/scripts/nexo-cognitive-decay 2.py +0 -182
  250. package/src/scripts/nexo-daily-self-audit 2.py +0 -552
  251. package/src/scripts/nexo-deep-sleep 2.sh +0 -97
  252. package/src/scripts/nexo-evolution-run 2.py +0 -597
  253. package/src/scripts/nexo-followup-hygiene 2.py +0 -112
  254. package/src/scripts/nexo-github-monitor 2.py +0 -256
  255. package/src/scripts/nexo-immune 2.py +0 -927
  256. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  257. package/src/scripts/nexo-install 2.py +0 -6
  258. package/src/scripts/nexo-learning-housekeep 2.py +0 -245
  259. package/src/scripts/nexo-learning-validator 2.py +0 -207
  260. package/src/scripts/nexo-migrate 2.py +0 -232
  261. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
  262. package/src/scripts/nexo-pre-commit 2.py +0 -120
  263. package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
  264. package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
  265. package/src/scripts/nexo-reflection 2.py +0 -253
  266. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  267. package/src/scripts/nexo-send-email 2.py +0 -25
  268. package/src/scripts/nexo-send-email.py +0 -25
  269. package/src/scripts/nexo-send-reply 2.py +0 -178
  270. package/src/scripts/nexo-send-reply.py +0 -178
  271. package/src/scripts/nexo-sleep 2.py +0 -592
  272. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  273. package/src/scripts/nexo-synthesis 2.py +0 -253
  274. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  275. package/src/scripts/nexo-update 2.sh +0 -161
  276. package/src/scripts/nexo-watchdog 2.sh +0 -878
  277. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  278. package/src/server 2.py +0 -733
  279. package/src/storage_router 2.py +0 -32
  280. package/src/tools_coordination 2.py +0 -102
  281. package/src/tools_credentials 2.py +0 -68
  282. package/src/tools_learnings 2.py +0 -220
  283. package/src/tools_menu 2.py +0 -227
  284. package/src/tools_reminders 2.py +0 -86
  285. package/src/tools_reminders_crud 2.py +0 -159
  286. package/src/tools_sessions 2.py +0 -476
  287. package/src/tools_task_history 2.py +0 -57
  288. package/templates/CLAUDE.md 2.template +0 -63
  289. package/templates/openclaw 2.json +0 -13
  290. package/tests/__init__ 2.py +0 -0
  291. package/tests/__init__.py +0 -0
  292. package/tests/conftest 2.py +0 -71
  293. package/tests/conftest.py +0 -71
  294. package/tests/test_cognitive 2.py +0 -205
  295. package/tests/test_cognitive.py +0 -205
  296. package/tests/test_knowledge_graph 2.py +0 -140
  297. package/tests/test_knowledge_graph.py +0 -140
  298. package/tests/test_migrations 2.py +0 -137
  299. package/tests/test_migrations.py +0 -137
@@ -1,120 +0,0 @@
1
- #!/usr/bin/env /Library/Frameworks/Python.framework/Versions/3.12/bin/python3
2
- """
3
- NEXO Pre-Commit Validator — Dynamic version
4
- Queries nexo.db learnings to surface relevant warnings/blockers before commit.
5
- Runs standalone (no MCP dependency, just sqlite3).
6
- """
7
- import os
8
- import sqlite3
9
- import subprocess
10
- import sys
11
- from pathlib import Path
12
-
13
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
14
- NEXO_DB = NEXO_HOME / "data" / "nexo.db"
15
-
16
-
17
- def get_staged_files():
18
- """Get list of staged files."""
19
- result = subprocess.run(
20
- ['git', 'diff', '--cached', '--name-only'],
21
- capture_output=True, text=True
22
- )
23
- return [f for f in result.stdout.strip().split('\n') if f]
24
-
25
-
26
- def check_file(filepath, conn):
27
- """Dynamic checks from learnings DB."""
28
- errors, warnings = [], []
29
-
30
- filename = Path(filepath).name
31
- parent_dir = Path(filepath).parent.name
32
-
33
- # 1. Find learnings mentioning this file or directory
34
- file_learnings = conn.execute(
35
- "SELECT id, title, content FROM learnings WHERE INSTR(content, ?) > 0 OR INSTR(content, ?) > 0",
36
- (filename, parent_dir)
37
- ).fetchall()
38
-
39
- # 2. Check repetition count for each matching learning
40
- for row in file_learnings:
41
- lid, title = row[0], row[1]
42
- rep_count = conn.execute(
43
- "SELECT COUNT(*) FROM error_repetitions WHERE original_learning_id = ?",
44
- (lid,)
45
- ).fetchone()[0]
46
-
47
- if rep_count >= 5:
48
- errors.append(f"BLOCKED #{lid} ({rep_count}x repeated): {title[:80]}")
49
- elif rep_count >= 3:
50
- warnings.append(f"WARNING #{lid} ({rep_count}x repeated): {title[:80]}")
51
-
52
- # 3. Universal rules (SIEMPRE/NUNCA/ANTES) matching this file
53
- universal = conn.execute(
54
- "SELECT id, title, content FROM learnings WHERE "
55
- "(content LIKE '%SIEMPRE%' OR content LIKE '%NUNCA%' OR content LIKE '%ANTES%') "
56
- "AND (INSTR(content, ?) > 0 OR INSTR(content, ?) > 0)",
57
- (filename, parent_dir)
58
- ).fetchall()
59
-
60
- for row in universal:
61
- lid, title = row[0], row[1]
62
- # Don't duplicate if already found above
63
- if not any(f"#{lid}" in e for e in errors + warnings):
64
- warnings.append(f"RULE #{lid}: {title[:80]}")
65
-
66
- return errors, warnings
67
-
68
-
69
- def main():
70
- """Run pre-commit validation."""
71
- staged = get_staged_files()
72
- if not staged:
73
- print("No staged files to check")
74
- return 0
75
-
76
- if not NEXO_DB.exists():
77
- print("nexo.db not found — skipping dynamic checks")
78
- return 0
79
-
80
- conn = sqlite3.connect(str(NEXO_DB), timeout=5)
81
-
82
- all_errors = {}
83
- all_warnings = {}
84
-
85
- for filepath in staged:
86
- errors, warnings = check_file(filepath, conn)
87
- if errors:
88
- all_errors[filepath] = errors
89
- if warnings:
90
- all_warnings[filepath] = warnings
91
-
92
- conn.close()
93
-
94
- # Print warnings (non-blocking)
95
- if all_warnings:
96
- print("\nWARNINGS:")
97
- for filepath, warns in all_warnings.items():
98
- print(f"\n {filepath}:")
99
- for w in warns:
100
- print(f" - {w}")
101
-
102
- # Print errors (blocking)
103
- if all_errors:
104
- print("\nBLOCKED — Fix these before committing:\n")
105
- for filepath, errs in all_errors.items():
106
- print(f" {filepath}:")
107
- for e in errs:
108
- print(f" - {e}")
109
- print()
110
- return 1
111
-
112
- if all_warnings:
113
- print("\nPre-commit passed with warnings\n")
114
- else:
115
- print("Pre-commit validation passed")
116
- return 0
117
-
118
-
119
- if __name__ == '__main__':
120
- sys.exit(main())
@@ -1,29 +0,0 @@
1
- #!/bin/bash
2
- # NEXO Prevent Sleep — keeps the machine awake so nocturnal processes run.
3
- #
4
- # macOS: uses caffeinate -s -i (prevent system + idle sleep)
5
- # Linux: uses systemd-inhibit or caffeine if available, otherwise no-op
6
- #
7
- # Run as LaunchAgent (KeepAlive) or systemd service.
8
-
9
- case "$(uname -s)" in
10
- Darwin)
11
- exec caffeinate -s -i -w $$
12
- ;;
13
- Linux)
14
- if command -v systemd-inhibit &>/dev/null; then
15
- exec systemd-inhibit --what=idle:sleep --who=nexo --why="NEXO nocturnal processes" sleep infinity
16
- elif command -v caffeine &>/dev/null; then
17
- exec caffeine
18
- else
19
- echo "[NEXO] No sleep prevention tool found. Install systemd-inhibit or caffeine."
20
- echo "[NEXO] Nocturnal processes may not run if the system sleeps."
21
- # Keep running so launchd/systemd doesn't restart loop
22
- exec sleep infinity
23
- fi
24
- ;;
25
- *)
26
- echo "[NEXO] Unsupported platform: $(uname -s)"
27
- exit 1
28
- ;;
29
- esac
@@ -1,345 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- NEXO Proactive Dashboard — Surfaces issues and opportunities without the user asking.
4
-
5
- Scans: overdue followups, forgotten reminders, unresolved learnings,
6
- inactive systems, user patterns, and more.
7
-
8
- Usage:
9
- python3 nexo-proactive-dashboard.py # Full scan, text output
10
- python3 nexo-proactive-dashboard.py --json # JSON output for programmatic use
11
- python3 nexo-proactive-dashboard.py --brief # One-liner alerts only
12
- """
13
-
14
- import json
15
- import os
16
- import sqlite3
17
- import subprocess
18
- import sys
19
- from datetime import datetime, timedelta
20
- from pathlib import Path
21
-
22
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
23
-
24
- NEXO_DB = NEXO_HOME / "data" / "nexo.db"
25
-
26
-
27
- def get_db():
28
- conn = sqlite3.connect(str(NEXO_DB), timeout=10)
29
- conn.row_factory = sqlite3.Row
30
- return conn
31
-
32
-
33
- def check_overdue_followups() -> list[dict]:
34
- """Find followups that are overdue and not completed."""
35
- conn = get_db()
36
- now_epoch = datetime.now().timestamp()
37
- rows = conn.execute("""
38
- SELECT id, description, date, created_at, reasoning
39
- FROM followups
40
- WHERE status NOT LIKE 'COMPLETED%'
41
- AND status NOT IN ('DELETED','archived','blocked','waiting')
42
- AND date IS NOT NULL AND date != ''
43
- ORDER BY date ASC
44
- """).fetchall()
45
- conn.close()
46
- alerts = []
47
- for r in rows:
48
- due_str = r["date"]
49
- try:
50
- due = datetime.fromisoformat(due_str) if due_str else None
51
- if due and due < datetime.now():
52
- days_overdue = (datetime.now() - due).days
53
- alerts.append({
54
- "type": "overdue_followup",
55
- "severity": "high" if days_overdue > 3 else "medium",
56
- "title": f"Followup overdue by {days_overdue}d: {r['description'][:80]}",
57
- "id": r["id"],
58
- "days_overdue": days_overdue,
59
- })
60
- except (ValueError, TypeError):
61
- pass
62
- return alerts
63
-
64
-
65
- def check_overdue_reminders() -> list[dict]:
66
- """Find reminders that are overdue."""
67
- conn = get_db()
68
- rows = conn.execute("""
69
- SELECT id, description, date, status
70
- FROM reminders
71
- WHERE status NOT IN ('COMPLETED', 'CANCELLED')
72
- AND date IS NOT NULL AND date != ''
73
- ORDER BY date ASC
74
- """).fetchall()
75
- conn.close()
76
- alerts = []
77
- for r in rows:
78
- due_str = r["date"]
79
- try:
80
- due = datetime.fromisoformat(due_str) if due_str else None
81
- if due and due < datetime.now():
82
- days_overdue = (datetime.now() - due).days
83
- alerts.append({
84
- "type": "overdue_reminder",
85
- "severity": "high" if days_overdue > 7 else "medium",
86
- "title": f"Reminder overdue by {days_overdue}d: {r['description'][:80]}",
87
- "id": r["id"],
88
- "days_overdue": days_overdue,
89
- })
90
- except (ValueError, TypeError):
91
- pass
92
- return alerts
93
-
94
-
95
- def check_stale_ideas() -> list[dict]:
96
- """Find reminders/ideas without due dates that have been sitting for too long."""
97
- conn = get_db()
98
- rows = conn.execute("""
99
- SELECT id, description, created_at
100
- FROM reminders
101
- WHERE status NOT IN ('COMPLETED', 'CANCELLED')
102
- AND (date IS NULL OR date = '')
103
- ORDER BY created_at ASC
104
- """).fetchall()
105
- conn.close()
106
- alerts = []
107
- stale_count = 0
108
- for r in rows:
109
- try:
110
- # created_at is epoch float
111
- created = datetime.fromtimestamp(r["created_at"])
112
- age_days = (datetime.now() - created).days
113
- except (ValueError, TypeError, OSError):
114
- age_days = 0
115
- if age_days > 14:
116
- stale_count += 1
117
-
118
- if stale_count > 10:
119
- alerts.append({
120
- "type": "stale_ideas",
121
- "severity": "low",
122
- "title": f"{stale_count} ideas/reminders without date have been sitting for >14 days. Review or archive.",
123
- "count": stale_count,
124
- })
125
- return alerts
126
-
127
-
128
- def check_session_gaps() -> list[dict]:
129
- """Detect if NEXO hasn't been active for unusual periods."""
130
- conn = get_db()
131
- row = conn.execute("""
132
- SELECT MAX(created_at) as last_diary FROM session_diary
133
- """).fetchone()
134
- conn.close()
135
- alerts = []
136
- if row and row["last_diary"]:
137
- try:
138
- last = datetime.fromisoformat(row["last_diary"])
139
- gap_hours = (datetime.now() - last).total_seconds() / 3600
140
- if gap_hours > 48:
141
- alerts.append({
142
- "type": "session_gap",
143
- "severity": "low",
144
- "title": f"No sessions recorded in {gap_hours:.0f}h ({gap_hours/24:.1f} days)",
145
- "gap_hours": gap_hours,
146
- })
147
- except (ValueError, TypeError):
148
- pass
149
- return alerts
150
-
151
-
152
- def check_evolution_status() -> list[dict]:
153
- """Check if evolution system is healthy."""
154
- alerts = []
155
- obj_file = NEXO_HOME / "brain" / "evolution-objective.json"
156
- if obj_file.exists():
157
- obj = json.loads(obj_file.read_text())
158
- if not obj.get("evolution_enabled", True):
159
- alerts.append({
160
- "type": "evolution_disabled",
161
- "severity": "high",
162
- "title": f"Evolution DISABLED: {obj.get('disabled_reason', 'unknown')}",
163
- })
164
- if obj.get("consecutive_failures", 0) > 0:
165
- alerts.append({
166
- "type": "evolution_failures",
167
- "severity": "medium",
168
- "title": f"Evolution: {obj['consecutive_failures']} consecutive failures",
169
- })
170
-
171
- # Check dimension regression
172
- for dim, data in obj.get("dimensions", {}).items():
173
- current = data.get("current", 0)
174
- if current < 30:
175
- alerts.append({
176
- "type": "dimension_low",
177
- "severity": "medium",
178
- "title": f"Dimension '{dim}' baja: {current}%",
179
- "dimension": dim,
180
- "score": current,
181
- })
182
- return alerts
183
-
184
-
185
- def check_pending_proposals() -> list[dict]:
186
- """Check for evolution proposals awaiting the user's review."""
187
- conn = get_db()
188
- rows = conn.execute("""
189
- SELECT id, dimension, proposal, created_at
190
- FROM evolution_log
191
- WHERE status = 'proposed' AND classification = 'propose'
192
- ORDER BY created_at DESC
193
- """).fetchall()
194
- conn.close()
195
- if rows:
196
- return [{
197
- "type": "pending_proposals",
198
- "severity": "low",
199
- "title": f"{len(rows)} evolution proposals pending review",
200
- "count": len(rows),
201
- "proposals": [{"id": r["id"], "dim": r["dimension"], "text": r["proposal"][:80]} for r in rows],
202
- }]
203
- return []
204
-
205
-
206
- def check_recurring_errors() -> list[dict]:
207
- """Detect learnings that keep appearing (same issue reported multiple times)."""
208
- conn = get_db()
209
- rows = conn.execute("""
210
- SELECT category, COUNT(*) as cnt
211
- FROM learnings
212
- WHERE created_at > datetime('now', '-7 days')
213
- GROUP BY category
214
- HAVING cnt >= 5
215
- ORDER BY cnt DESC
216
- """).fetchall()
217
- conn.close()
218
- alerts = []
219
- for r in rows:
220
- alerts.append({
221
- "type": "recurring_errors",
222
- "severity": "medium",
223
- "title": f"Category '{r['category']}' has {r['cnt']} learnings this week — possible systemic issue",
224
- "category": r["category"],
225
- "count": r["cnt"],
226
- })
227
- return alerts
228
-
229
-
230
- def check_cron_health() -> list[dict]:
231
- """Check if critical cron jobs are running."""
232
- alerts = []
233
-
234
- # Check backup cron
235
- backup_dir = NEXO_HOME / "backups"
236
- if backup_dir.exists():
237
- backups = sorted(backup_dir.glob("nexo-*.db"), key=lambda p: p.stat().st_mtime, reverse=True)
238
- if backups:
239
- last_backup_age = (datetime.now().timestamp() - backups[0].stat().st_mtime) / 3600
240
- if last_backup_age > 4:
241
- alerts.append({
242
- "type": "backup_stale",
243
- "severity": "high",
244
- "title": f"Last nexo.db backup {last_backup_age:.1f}h (should be hourly)",
245
- })
246
-
247
- # Check immune system
248
- immune_status = NEXO_HOME / "coordination" / "immune-status.json"
249
- if immune_status.exists():
250
- try:
251
- status = json.loads(immune_status.read_text())
252
- if status.get("status") == "degraded":
253
- alerts.append({
254
- "type": "immune_degraded",
255
- "severity": "high",
256
- "title": f"Immune system degraded: {status.get('reason', '?')}",
257
- })
258
- except (json.JSONDecodeError, KeyError):
259
- pass
260
-
261
- return alerts
262
-
263
-
264
- def run_all_checks() -> list[dict]:
265
- """Run all proactive checks and return sorted alerts."""
266
- all_alerts = []
267
- checks = [
268
- check_overdue_followups,
269
- check_overdue_reminders,
270
- check_stale_ideas,
271
- check_session_gaps,
272
- check_evolution_status,
273
- check_pending_proposals,
274
- check_recurring_errors,
275
- check_cron_health,
276
- ]
277
-
278
- for check in checks:
279
- try:
280
- all_alerts.extend(check())
281
- except Exception as e:
282
- all_alerts.append({
283
- "type": "check_error",
284
- "severity": "low",
285
- "title": f"Check {check.__name__} failed: {e}",
286
- })
287
-
288
- # Sort by severity
289
- severity_order = {"high": 0, "medium": 1, "low": 2}
290
- all_alerts.sort(key=lambda a: severity_order.get(a.get("severity", "low"), 3))
291
-
292
- return all_alerts
293
-
294
-
295
- def format_text(alerts: list[dict]) -> str:
296
- """Format alerts as readable text."""
297
- if not alerts:
298
- return "No proactive alerts. All clear."
299
-
300
- severity_icons = {"high": "!!!", "medium": " ! ", "low": " . "}
301
- lines = [f"NEXO Proactive Dashboard — {len(alerts)} alerts\n"]
302
-
303
- current_severity = None
304
- for a in alerts:
305
- sev = a.get("severity", "low")
306
- if sev != current_severity:
307
- current_severity = sev
308
- label = {"high": "URGENTE", "medium": "ATENCION", "low": "INFO"}.get(sev, sev)
309
- lines.append(f"\n [{label}]")
310
- icon = severity_icons.get(sev, " . ")
311
- lines.append(f" {icon} {a['title']}")
312
-
313
- return "\n".join(lines)
314
-
315
-
316
- def format_brief(alerts: list[dict]) -> str:
317
- """One-liner summary."""
318
- high = sum(1 for a in alerts if a.get("severity") == "high")
319
- med = sum(1 for a in alerts if a.get("severity") == "medium")
320
- low = sum(1 for a in alerts if a.get("severity") == "low")
321
- if not alerts:
322
- return "Dashboard: clean"
323
- return f"Dashboard: {high} urgent, {med} attention, {low} info"
324
-
325
-
326
- def main():
327
- output_json = "--json" in sys.argv
328
- brief = "--brief" in sys.argv
329
-
330
- alerts = run_all_checks()
331
-
332
- if output_json:
333
- print(json.dumps(alerts, indent=2, default=str))
334
- elif brief:
335
- print(format_brief(alerts))
336
- else:
337
- print(format_text(alerts))
338
-
339
- # Exit code = number of high severity alerts
340
- high_count = sum(1 for a in alerts if a.get("severity") == "high")
341
- sys.exit(min(high_count, 125))
342
-
343
-
344
- if __name__ == "__main__":
345
- main()