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.
- package/README.md +1 -1
- package/bin/nexo-brain.js +92 -9
- package/bin/postinstall.js +22 -15
- package/package.json +7 -4
- package/src/auto_update.py +194 -5
- package/src/crons/sync.py +6 -2
- package/src/db/_core.py +1 -0
- package/src/db/_entities.py +1 -0
- package/src/db/_episodic.py +1 -0
- package/src/db/_learnings.py +1 -0
- package/src/db/_reminders.py +1 -0
- package/src/db/_schema.py +11 -1
- package/src/db/_sessions.py +1 -0
- package/src/db/_skills.py +1 -0
- package/src/hooks/capture-tool-logs.sh +23 -6
- package/src/hooks/session-start.sh +4 -3
- package/src/plugin_loader.py +1 -0
- package/src/plugins/update.py +377 -26
- package/src/scripts/deep-sleep/apply_findings.py +1 -0
- package/src/scripts/deep-sleep/collect.py +1 -0
- package/src/scripts/deep-sleep/extract.py +1 -0
- package/src/scripts/deep-sleep/synthesize.py +1 -0
- package/src/scripts/nexo-catchup.py +29 -4
- package/src/scripts/nexo-daily-self-audit.py +21 -1
- package/src/scripts/nexo-evolution-run.py +21 -1
- package/src/scripts/nexo-learning-housekeep.py +1 -0
- package/src/scripts/nexo-postmortem-consolidator.py +34 -9
- package/src/scripts/nexo-sleep.py +32 -10
- package/src/scripts/nexo-synthesis.py +29 -9
- package/src/scripts/nexo-update.sh +109 -7
- package/src/scripts/nexo-watchdog.sh +122 -58
- package/src/server.py +66 -1
- package/src/tools_coordination.py +1 -0
- package/src/tools_sessions.py +1 -0
- package/scripts/migrate-to-unified 2.sh +0 -813
- package/scripts/migrate-to-unified.sh +0 -813
- package/scripts/migrate-v1.5-to-v1.6 2.py +0 -778
- package/scripts/migrate-v1.5-to-v1.6.py +0 -778
- package/scripts/migrate-v1.7-to-v1.8 2.py +0 -214
- package/scripts/migrate-v1.7-to-v1.8.py +0 -214
- package/scripts/nexo-preflight.sh +0 -236
- package/scripts/pre-commit-check 2.sh +0 -55
- package/scripts/pre-commit-check.sh +0 -55
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
- package/src/auto_close_sessions 2.py +0 -159
- package/src/auto_update 2.py +0 -634
- package/src/claim_graph 2.py +0 -323
- package/src/cognitive/__init__ 2.py +0 -62
- package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
- package/src/cognitive/_core 2.py +0 -567
- package/src/cognitive/_decay 2.py +0 -382
- package/src/cognitive/_ingest 2.py +0 -892
- package/src/cognitive/_memory 2.py +0 -912
- package/src/cognitive/_search 2.py +0 -949
- package/src/cognitive/_trust 2.py +0 -464
- package/src/crons/__pycache__/sync.cpython-314.pyc +0 -0
- package/src/crons/manifest 2.json +0 -106
- package/src/crons/sync 2.py +0 -217
- package/src/dashboard/__init__ 2.py +0 -0
- package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
- package/src/dashboard/app 2.py +0 -789
- package/src/db/__init__ 2.py +0 -89
- package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_cron_runs.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_cron_runs.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_skills.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_skills.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_skills.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
- package/src/db/_core 2.py +0 -417
- package/src/db/_credentials 2.py +0 -124
- package/src/db/_entities 2.py +0 -178
- package/src/db/_episodic 2.py +0 -738
- package/src/db/_evolution 2.py +0 -54
- package/src/db/_fts 2.py +0 -406
- package/src/db/_learnings 2.py +0 -168
- package/src/db/_reminders 2.py +0 -338
- package/src/db/_schema 2.py +0 -364
- package/src/db/_sessions 2.py +0 -300
- package/src/db/_tasks 2.py +0 -91
- package/src/evolution_cycle 2.py +0 -266
- package/src/hnsw_index 2.py +0 -254
- package/src/hooks/auto_capture 2.py +0 -208
- package/src/hooks/caffeinate-guard 2.sh +0 -8
- package/src/hooks/capture-session 2.sh +0 -21
- package/src/hooks/capture-tool-logs 2.sh +0 -127
- package/src/hooks/daily-briefing-check 2.sh +0 -33
- package/src/hooks/inbox-hook 2.sh +0 -76
- package/src/hooks/post-compact 2.sh +0 -148
- package/src/hooks/pre-compact 2.sh +0 -151
- package/src/hooks/session-start 2.sh +0 -268
- package/src/hooks/session-stop 2.sh +0 -140
- package/src/kg_populate 2.py +0 -290
- package/src/knowledge_graph 2.py +0 -257
- package/src/maintenance 2.py +0 -59
- package/src/migrate_embeddings 2.py +0 -122
- package/src/plugin_loader 2.py +0 -202
- package/src/plugins/__init__ 2.py +0 -0
- package/src/plugins/__pycache__/__init__ 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/schedule.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/schedule.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/skills.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/skills.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/update 2.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/adaptive_mode 2.py +0 -805
- package/src/plugins/agents 2.py +0 -52
- package/src/plugins/artifact_registry 2.py +0 -450
- package/src/plugins/backup 2.py +0 -104
- package/src/plugins/cognitive_memory 2.py +0 -564
- package/src/plugins/core_rules 2.py +0 -252
- package/src/plugins/cortex 2.py +0 -299
- package/src/plugins/entities 2.py +0 -67
- package/src/plugins/episodic_memory 2.py +0 -533
- package/src/plugins/evolution 2.py +0 -115
- package/src/plugins/guard 2.py +0 -746
- package/src/plugins/knowledge_graph_tools 2.py +0 -105
- package/src/plugins/preferences 2.py +0 -47
- package/src/plugins/update 2.py +0 -256
- package/src/requirements 2.txt +0 -12
- package/src/rules/__init__ 2.py +0 -0
- package/src/rules/core-rules 2.json +0 -331
- package/src/rules/migrate 2.py +0 -207
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/check-context 2.py +0 -264
- package/src/scripts/nexo-auto-update 2.py +0 -6
- package/src/scripts/nexo-backup 2.sh +0 -25
- package/src/scripts/nexo-brain-activation 2.sh +0 -140
- package/src/scripts/nexo-catchup 2.py +0 -242
- package/src/scripts/nexo-cognitive-decay 2.py +0 -182
- package/src/scripts/nexo-daily-self-audit 2.py +0 -552
- package/src/scripts/nexo-deep-sleep 2.sh +0 -97
- package/src/scripts/nexo-evolution-run 2.py +0 -597
- package/src/scripts/nexo-followup-hygiene 2.py +0 -112
- package/src/scripts/nexo-github-monitor 2.py +0 -256
- package/src/scripts/nexo-immune 2.py +0 -927
- package/src/scripts/nexo-inbox-hook 2.sh +0 -74
- package/src/scripts/nexo-install 2.py +0 -6
- package/src/scripts/nexo-learning-housekeep 2.py +0 -245
- package/src/scripts/nexo-learning-validator 2.py +0 -207
- package/src/scripts/nexo-migrate 2.py +0 -232
- package/src/scripts/nexo-postmortem-consolidator 2.py +0 -421
- package/src/scripts/nexo-pre-commit 2.py +0 -120
- package/src/scripts/nexo-prevent-sleep 2.sh +0 -29
- package/src/scripts/nexo-proactive-dashboard 2.py +0 -345
- package/src/scripts/nexo-reflection 2.py +0 -253
- package/src/scripts/nexo-runtime-preflight 2.py +0 -274
- package/src/scripts/nexo-send-email 2.py +0 -25
- package/src/scripts/nexo-send-email.py +0 -25
- package/src/scripts/nexo-send-reply 2.py +0 -178
- package/src/scripts/nexo-send-reply.py +0 -178
- package/src/scripts/nexo-sleep 2.py +0 -592
- package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
- package/src/scripts/nexo-synthesis 2.py +0 -253
- package/src/scripts/nexo-tcc-approve 2.sh +0 -79
- package/src/scripts/nexo-update 2.sh +0 -161
- package/src/scripts/nexo-watchdog 2.sh +0 -878
- package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
- package/src/server 2.py +0 -733
- package/src/storage_router 2.py +0 -32
- package/src/tools_coordination 2.py +0 -102
- package/src/tools_credentials 2.py +0 -68
- package/src/tools_learnings 2.py +0 -220
- package/src/tools_menu 2.py +0 -227
- package/src/tools_reminders 2.py +0 -86
- package/src/tools_reminders_crud 2.py +0 -159
- package/src/tools_sessions 2.py +0 -476
- package/src/tools_task_history 2.py +0 -57
- package/templates/CLAUDE.md 2.template +0 -63
- package/templates/openclaw 2.json +0 -13
- package/tests/__init__ 2.py +0 -0
- package/tests/__init__.py +0 -0
- package/tests/conftest 2.py +0 -71
- package/tests/conftest.py +0 -71
- package/tests/test_cognitive 2.py +0 -205
- package/tests/test_cognitive.py +0 -205
- package/tests/test_knowledge_graph 2.py +0 -140
- package/tests/test_knowledge_graph.py +0 -140
- package/tests/test_migrations 2.py +0 -137
- 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")
|