nexo-brain 2.3.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -8
- package/bin/nexo-brain.js +230 -22
- package/bin/nexo.js +55 -0
- package/community/skills/.gitkeep +1 -0
- package/package.json +5 -2
- package/src/auto_update.py +158 -8
- package/src/cli.py +605 -0
- package/src/cognitive/_ingest.py +1 -1
- package/src/cognitive/_memory.py +4 -4
- package/src/crons/manifest.json +8 -0
- package/src/dashboard/app.py +709 -37
- package/src/dashboard/templates/adaptive.html +112 -218
- package/src/dashboard/templates/artifacts.html +133 -0
- package/src/dashboard/templates/backups.html +136 -0
- package/src/dashboard/templates/base.html +413 -0
- package/src/dashboard/templates/calendar.html +523 -652
- package/src/dashboard/templates/chat.html +356 -0
- package/src/dashboard/templates/claims.html +259 -0
- package/src/dashboard/templates/cortex.html +262 -0
- package/src/dashboard/templates/credentials.html +128 -0
- package/src/dashboard/templates/crons.html +370 -0
- package/src/dashboard/templates/dashboard.html +384 -572
- package/src/dashboard/templates/dreams.html +252 -0
- package/src/dashboard/templates/email.html +160 -0
- package/src/dashboard/templates/evolution.html +189 -0
- package/src/dashboard/templates/feed.html +249 -0
- package/src/dashboard/templates/followup_health.html +170 -0
- package/src/dashboard/templates/graph.html +191 -269
- package/src/dashboard/templates/guard.html +259 -0
- package/src/dashboard/templates/inbox.html +220 -336
- package/src/dashboard/templates/memory.html +317 -197
- package/src/dashboard/templates/operations.html +498 -652
- package/src/dashboard/templates/plugins.html +185 -0
- package/src/dashboard/templates/rules.html +246 -0
- package/src/dashboard/templates/sentiment.html +247 -0
- package/src/dashboard/templates/sessions.html +215 -171
- package/src/dashboard/templates/skills.html +329 -0
- package/src/dashboard/templates/somatic.html +68 -172
- package/src/dashboard/templates/triggers.html +133 -0
- package/src/dashboard/templates/trust.html +360 -0
- package/src/db/__init__.py +5 -0
- package/src/db/_schema.py +25 -1
- package/src/db/_sessions.py +22 -0
- package/src/db/_skills.py +983 -252
- package/src/doctor/__init__.py +1 -0
- package/src/doctor/formatters.py +52 -0
- package/src/doctor/models.py +44 -0
- package/src/doctor/orchestrator.py +42 -0
- package/src/doctor/providers/__init__.py +1 -0
- package/src/doctor/providers/boot.py +206 -0
- package/src/doctor/providers/deep.py +292 -0
- package/src/doctor/providers/runtime.py +686 -0
- package/src/hooks/capture-tool-logs.sh +18 -4
- package/src/hooks/post-compact.sh +5 -1
- package/src/hooks/pre-compact.sh +1 -1
- package/src/plugin_loader.py +14 -0
- package/src/plugins/doctor.py +36 -0
- package/src/plugins/evolution.py +2 -1
- package/src/plugins/skills.py +135 -175
- package/src/requirements.txt +1 -0
- package/src/script_registry.py +322 -0
- package/src/scripts/deep-sleep/apply_findings.py +63 -33
- package/src/scripts/deep-sleep/collect.py +38 -9
- package/src/scripts/deep-sleep/extract-prompt.md +14 -0
- package/src/scripts/deep-sleep/synthesize-prompt.md +36 -0
- package/src/scripts/deep-sleep/synthesize.py +37 -1
- package/src/scripts/nexo-dashboard.sh +29 -0
- package/src/scripts/nexo-day-orchestrator.sh +139 -0
- package/src/scripts/nexo-evolution-run.py +2 -1
- package/src/scripts/nexo-learning-housekeep.py +1 -1
- package/src/scripts/nexo-watchdog.sh +1 -1
- package/src/server.py +9 -5
- package/src/skills/run-runtime-doctor/guide.md +12 -0
- package/src/skills/run-runtime-doctor/script.py +21 -0
- package/src/skills/run-runtime-doctor/skill.json +25 -0
- package/src/skills_runtime.py +347 -0
- package/src/tools_menu.py +3 -2
- package/src/tools_sessions.py +126 -0
- package/src/user_context.py +46 -0
- package/templates/nexo_helper.py +45 -0
- package/templates/script-template.py +44 -0
- package/templates/skill-script-template.py +39 -0
- package/templates/skill-template.md +33 -0
|
@@ -19,9 +19,13 @@ from datetime import datetime
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
21
|
NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
|
|
22
|
+
NEXO_CODE = Path(os.environ.get("NEXO_CODE", str(Path(__file__).resolve().parents[2])))
|
|
22
23
|
DEEP_SLEEP_DIR = NEXO_HOME / "operations" / "deep-sleep"
|
|
23
24
|
PROMPT_FILE = Path(__file__).parent / "synthesize-prompt.md"
|
|
24
25
|
|
|
26
|
+
if str(NEXO_CODE) not in sys.path:
|
|
27
|
+
sys.path.insert(0, str(NEXO_CODE))
|
|
28
|
+
|
|
25
29
|
CLAUDE_TIMEOUT = 21600 # 3h safety net (prevents zombie processes)
|
|
26
30
|
|
|
27
31
|
|
|
@@ -71,6 +75,31 @@ def extract_json_from_response(text: str) -> dict | None:
|
|
|
71
75
|
return None
|
|
72
76
|
|
|
73
77
|
|
|
78
|
+
def collect_skill_runtime_candidates(target_date: str) -> tuple[Path, dict]:
|
|
79
|
+
"""Collect mature skill candidates from DB usage so Deep Sleep can evolve them."""
|
|
80
|
+
output_file = DEEP_SLEEP_DIR / f"{target_date}-skill-runtime-candidates.json"
|
|
81
|
+
payload = {
|
|
82
|
+
"scriptable": [],
|
|
83
|
+
"improvements": [],
|
|
84
|
+
}
|
|
85
|
+
try:
|
|
86
|
+
from db import (
|
|
87
|
+
collect_scriptable_skill_candidates,
|
|
88
|
+
collect_skill_improvement_candidates,
|
|
89
|
+
init_db,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
init_db()
|
|
93
|
+
payload["scriptable"] = collect_scriptable_skill_candidates()
|
|
94
|
+
payload["improvements"] = collect_skill_improvement_candidates()
|
|
95
|
+
except Exception as e:
|
|
96
|
+
payload["error"] = str(e)
|
|
97
|
+
|
|
98
|
+
with open(output_file, "w") as f:
|
|
99
|
+
json.dump(payload, f, indent=2, ensure_ascii=False)
|
|
100
|
+
return output_file, payload
|
|
101
|
+
|
|
102
|
+
|
|
74
103
|
def main():
|
|
75
104
|
target_date = sys.argv[1] if len(sys.argv) > 1 else datetime.now().strftime("%Y-%m-%d")
|
|
76
105
|
|
|
@@ -86,7 +115,10 @@ def main():
|
|
|
86
115
|
extractions = json.load(f)
|
|
87
116
|
|
|
88
117
|
total_findings = extractions.get("total_findings", 0)
|
|
89
|
-
|
|
118
|
+
runtime_candidates_file, runtime_candidates = collect_skill_runtime_candidates(target_date)
|
|
119
|
+
runtime_candidate_count = len(runtime_candidates.get("scriptable", [])) + len(runtime_candidates.get("improvements", []))
|
|
120
|
+
|
|
121
|
+
if total_findings == 0 and runtime_candidate_count == 0:
|
|
90
122
|
print(f"[synthesize] No findings to synthesize for {target_date}.")
|
|
91
123
|
# Write minimal synthesis
|
|
92
124
|
output = {
|
|
@@ -95,6 +127,8 @@ def main():
|
|
|
95
127
|
"cross_session_patterns": [],
|
|
96
128
|
"morning_agenda": [],
|
|
97
129
|
"context_packets": [],
|
|
130
|
+
"skills": [],
|
|
131
|
+
"skill_evolution_candidates": [],
|
|
98
132
|
"actions": [],
|
|
99
133
|
"summary": f"No significant findings for {target_date}."
|
|
100
134
|
}
|
|
@@ -108,9 +142,11 @@ def main():
|
|
|
108
142
|
prompt_template = PROMPT_FILE.read_text()
|
|
109
143
|
prompt = prompt_template.replace("{{EXTRACTIONS_FILE}}", str(extractions_file))
|
|
110
144
|
prompt = prompt.replace("{{CONTEXT_FILE}}", str(context_file))
|
|
145
|
+
prompt = prompt.replace("{{SKILL_RUNTIME_FILE}}", str(runtime_candidates_file))
|
|
111
146
|
|
|
112
147
|
claude_bin = find_claude_cli()
|
|
113
148
|
print(f"[synthesize] Phase 3: Synthesizing {total_findings} findings from {target_date}")
|
|
149
|
+
print(f"[synthesize] Skill runtime candidates: {runtime_candidate_count}")
|
|
114
150
|
print(f"[synthesize] Claude CLI: {claude_bin}")
|
|
115
151
|
|
|
116
152
|
try:
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# NEXO Dashboard — Web UI at localhost:6174
|
|
4
|
+
# Schedule: keepAlive (persistent daemon, auto-restart on crash)
|
|
5
|
+
# ============================================================================
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
|
|
9
|
+
NEXO_CODE="${NEXO_CODE:-$NEXO_HOME}"
|
|
10
|
+
|
|
11
|
+
# Find Python
|
|
12
|
+
if [ -x "$NEXO_HOME/.venv/bin/python3" ]; then
|
|
13
|
+
PYTHON="$NEXO_HOME/.venv/bin/python3"
|
|
14
|
+
else
|
|
15
|
+
PYTHON="python3"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Dashboard module location: prefer NEXO_CODE (repo), fallback NEXO_HOME (installed)
|
|
19
|
+
if [ -f "$NEXO_CODE/dashboard/app.py" ]; then
|
|
20
|
+
DASH_DIR="$NEXO_CODE"
|
|
21
|
+
elif [ -f "$NEXO_HOME/dashboard/app.py" ]; then
|
|
22
|
+
DASH_DIR="$NEXO_HOME"
|
|
23
|
+
else
|
|
24
|
+
echo "Dashboard not found" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
cd "$DASH_DIR"
|
|
29
|
+
exec "$PYTHON" -m dashboard.app --no-browser --port 6174
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# NEXO Day Orchestrator — autonomous NEXO cycle every 15 min
|
|
4
|
+
# Schedule: keepAlive, self-enforced operating hours (default 8:00-23:00)
|
|
5
|
+
#
|
|
6
|
+
# This is NOT a Python script that simulates intelligence.
|
|
7
|
+
# This launches Claude Code as NEXO with full MCP access.
|
|
8
|
+
# NEXO thinks, acts, and reports — like any interactive session.
|
|
9
|
+
# ============================================================================
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
NEXO_HOME="${NEXO_HOME:-$HOME/.nexo}"
|
|
13
|
+
LOG_DIR="$NEXO_HOME/logs"
|
|
14
|
+
mkdir -p "$LOG_DIR" "$NEXO_HOME/operations"
|
|
15
|
+
|
|
16
|
+
# --- Configuration ---
|
|
17
|
+
CYCLE_INTERVAL=900 # 15 minutes between cycles
|
|
18
|
+
CYCLE_TIMEOUT=600 # 10 min max per cycle
|
|
19
|
+
MAX_TURNS=30 # Claude max turns per cycle
|
|
20
|
+
HOUR_START=8
|
|
21
|
+
HOUR_END=23
|
|
22
|
+
|
|
23
|
+
# --- Find Claude CLI ---
|
|
24
|
+
find_claude() {
|
|
25
|
+
for candidate in \
|
|
26
|
+
"$(command -v claude 2>/dev/null)" \
|
|
27
|
+
"$HOME/.claude/local/claude" \
|
|
28
|
+
"/opt/homebrew/bin/claude" \
|
|
29
|
+
"/usr/local/bin/claude"; do
|
|
30
|
+
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
|
|
31
|
+
echo "$candidate"
|
|
32
|
+
return 0
|
|
33
|
+
fi
|
|
34
|
+
done
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
CLAUDE=$(find_claude) || {
|
|
39
|
+
echo "$(date '+%Y-%m-%d %H:%M') ERROR: claude CLI not found" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# --- Prevent overlapping cycles ---
|
|
44
|
+
LOCKFILE="$NEXO_HOME/operations/.orchestrator.lock"
|
|
45
|
+
acquire_lock() {
|
|
46
|
+
if [ -f "$LOCKFILE" ]; then
|
|
47
|
+
local pid
|
|
48
|
+
pid=$(cat "$LOCKFILE" 2>/dev/null || echo "")
|
|
49
|
+
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
50
|
+
return 1 # Still running
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
echo $$ > "$LOCKFILE"
|
|
54
|
+
return 0
|
|
55
|
+
}
|
|
56
|
+
release_lock() { rm -f "$LOCKFILE"; }
|
|
57
|
+
|
|
58
|
+
# --- The orchestrator prompt ---
|
|
59
|
+
PROMPT='You are NEXO in autonomous orchestrator mode. The user is NOT present. You have 5 minutes max.
|
|
60
|
+
|
|
61
|
+
ABSOLUTE PRIORITY: act, do not list. If you can do something, do it. If you need the user, send email.
|
|
62
|
+
|
|
63
|
+
CHECKLIST (in this order):
|
|
64
|
+
|
|
65
|
+
1. OVERDUE FOLLOWUPS: nexo_reminders(filter="due") + nexo_reminders(filter="followups")
|
|
66
|
+
- NEXO tasks (verify, check, monitor) → DO THEM NOW
|
|
67
|
+
- Tasks needing user decision → accumulate for email
|
|
68
|
+
- Completed ones → nexo_followup_complete
|
|
69
|
+
|
|
70
|
+
2. EMAIL: nexo_email_inbox(unread_only=true, limit=10)
|
|
71
|
+
- Emails you can process → process them
|
|
72
|
+
- Important emails for user → accumulate for email
|
|
73
|
+
|
|
74
|
+
3. INFRASTRUCTURE: nexo_doctor(tier="runtime")
|
|
75
|
+
- If degraded/critical → try to fix
|
|
76
|
+
|
|
77
|
+
4. EMAIL TO USER (only if there is something to report):
|
|
78
|
+
- nexo_email_send with clean HTML summary
|
|
79
|
+
- Only what needs attention or decision
|
|
80
|
+
- Include what you ALREADY DID (not just pending items)
|
|
81
|
+
- If nothing relevant → DO NOT send email
|
|
82
|
+
- Max 1 email per cycle
|
|
83
|
+
|
|
84
|
+
5. DIARY: nexo_session_diary_write with what you did
|
|
85
|
+
|
|
86
|
+
RULES:
|
|
87
|
+
- DO NOT ask permission. autonomy=full
|
|
88
|
+
- DO NOT send empty or "all ok" emails
|
|
89
|
+
- DO NOT list things without acting
|
|
90
|
+
- If a followup is executable → execute it before reporting
|
|
91
|
+
- Use nexo_heartbeat at start
|
|
92
|
+
- Clean close: diary + nexo_stop'
|
|
93
|
+
|
|
94
|
+
# --- Main loop ---
|
|
95
|
+
echo "$(date '+%Y-%m-%d %H:%M') NEXO Day Orchestrator starting (PID $$)"
|
|
96
|
+
echo " Claude: $CLAUDE"
|
|
97
|
+
echo " Cycle: every ${CYCLE_INTERVAL}s, ${HOUR_START}:00-${HOUR_END}:00"
|
|
98
|
+
echo " Timeout: ${CYCLE_TIMEOUT}s, max turns: $MAX_TURNS"
|
|
99
|
+
|
|
100
|
+
while true; do
|
|
101
|
+
HOUR=$(date +%H | sed 's/^0//')
|
|
102
|
+
|
|
103
|
+
# Outside operating hours — sleep and check again
|
|
104
|
+
if [ "$HOUR" -lt "$HOUR_START" ] || [ "$HOUR" -ge "$HOUR_END" ]; then
|
|
105
|
+
sleep 300 # Check every 5 min if we're back in hours
|
|
106
|
+
continue
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Try to acquire lock
|
|
110
|
+
if ! acquire_lock; then
|
|
111
|
+
echo "$(date '+%Y-%m-%d %H:%M') Previous cycle still running. Skipping."
|
|
112
|
+
sleep "$CYCLE_INTERVAL"
|
|
113
|
+
continue
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
TIMESTAMP=$(date '+%Y-%m-%d_%H%M')
|
|
117
|
+
LOGFILE="$LOG_DIR/orchestrator-$TIMESTAMP.log"
|
|
118
|
+
echo "$(date '+%Y-%m-%d %H:%M') Cycle starting..."
|
|
119
|
+
|
|
120
|
+
# Launch Claude Code as NEXO
|
|
121
|
+
set +e
|
|
122
|
+
timeout "$CYCLE_TIMEOUT" "$CLAUDE" \
|
|
123
|
+
--dangerously-skip-permissions \
|
|
124
|
+
-p "$PROMPT" \
|
|
125
|
+
--max-turns "$MAX_TURNS" \
|
|
126
|
+
>>"$LOGFILE" 2>&1
|
|
127
|
+
EXIT_CODE=$?
|
|
128
|
+
set -e
|
|
129
|
+
|
|
130
|
+
echo "$(date '+%Y-%m-%d %H:%M') Cycle finished (exit $EXIT_CODE)" | tee -a "$LOGFILE"
|
|
131
|
+
|
|
132
|
+
release_lock
|
|
133
|
+
|
|
134
|
+
# Clean old logs (keep 7 days)
|
|
135
|
+
find "$LOG_DIR" -name "orchestrator-*.log" -mtime +7 -delete 2>/dev/null || true
|
|
136
|
+
|
|
137
|
+
# Sleep until next cycle
|
|
138
|
+
sleep "$CYCLE_INTERVAL"
|
|
139
|
+
done
|
|
@@ -45,9 +45,10 @@ AUTO_SAFE_PREFIXES = [
|
|
|
45
45
|
str(CLAUDE_DIR / "coordination") + "/",
|
|
46
46
|
]
|
|
47
47
|
|
|
48
|
-
# Public mode:
|
|
48
|
+
# Public mode: user scripts and plugins only — NEVER core code
|
|
49
49
|
AUTO_SAFE_PREFIXES_PUBLIC = [
|
|
50
50
|
str(CLAUDE_DIR / "scripts") + "/",
|
|
51
|
+
str(CLAUDE_DIR / "plugins") + "/",
|
|
51
52
|
]
|
|
52
53
|
|
|
53
54
|
# ── Immutable files — NEVER touch (applies to ALL modes) ────────────────
|
|
@@ -70,7 +70,7 @@ def adjust_weights(conn):
|
|
|
70
70
|
priority = l["priority"] or "medium"
|
|
71
71
|
|
|
72
72
|
# Priority floor — critical learnings never drop below 0.5
|
|
73
|
-
priority_floor = {"critical": 0.5, "high": 0.3, "medium": 0.1, "low": 0.05}
|
|
73
|
+
priority_floor = {"critical": 0.5, "high": 0.3, "medium": 0.1, "low": 0.05}.get(priority, 0.1)
|
|
74
74
|
|
|
75
75
|
new_weight = old_weight
|
|
76
76
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# ============================================================================
|
|
3
3
|
# NEXO Watchdog — Comprehensive health monitor for all NEXO services
|
|
4
|
-
#
|
|
4
|
+
# Schedule: every 30 minutes (interval_seconds: 1800)
|
|
5
5
|
# ============================================================================
|
|
6
6
|
# Monitors ALL LaunchAgents, cron jobs, and background processes.
|
|
7
7
|
# Outputs: watchdog-status.json (machine), watchdog-report.txt (human),
|
package/src/server.py
CHANGED
|
@@ -8,6 +8,7 @@ import sys
|
|
|
8
8
|
from fastmcp import FastMCP
|
|
9
9
|
from db import init_db, rebuild_fts_index, get_db, close_db, fts_add_dir, fts_remove_dir, fts_list_dirs
|
|
10
10
|
from tools_sessions import handle_startup, handle_heartbeat, handle_status, handle_context_packet, handle_smart_startup_query
|
|
11
|
+
from user_context import get_context as _get_ctx
|
|
11
12
|
from tools_coordination import (
|
|
12
13
|
handle_track, handle_untrack, handle_files,
|
|
13
14
|
handle_send, handle_ask, handle_answer, handle_check_answer,
|
|
@@ -154,12 +155,17 @@ def _server_init():
|
|
|
154
155
|
mcp = FastMCP(
|
|
155
156
|
name="nexo",
|
|
156
157
|
instructions=(
|
|
157
|
-
"
|
|
158
|
+
f"{_get_ctx().assistant_name} — cognitive co-operator. Save important info from tool results before they clear.\n\n"
|
|
159
|
+
"## CRITICAL — do these or you WILL get corrected\n"
|
|
160
|
+
"- **Guard (MANDATORY before ANY code edit):** `nexo_guard_check(files='...', area='...')` BEFORE editing code. "
|
|
161
|
+
"No exceptions. Blocking rules→resolve first. `nexo_track(sid=SID, paths=[...])` before shared files\n"
|
|
162
|
+
"- **Skills (MANDATORY before multi-step tasks):** `nexo_skill_match(task)` to find reusable procedures. "
|
|
163
|
+
"If match found, read it and follow the steps. After completion, `nexo_skill_result(id, success, context)` to record outcome.\n"
|
|
164
|
+
"- **Learnings (MANDATORY on corrections):** When you discover a bug, pattern, or get corrected→`nexo_learning_add` IMMEDIATELY. "
|
|
165
|
+
"Do NOT batch. Do NOT wait until end of session.\n\n"
|
|
158
166
|
"## Rules\n"
|
|
159
167
|
"- **Heartbeat:** `nexo_heartbeat(sid=SID, task='...', context_hint='...')` every user msg. "
|
|
160
168
|
"React: DIARY REMINDER→write diary, VIBE:NEGATIVE→ultra-concise, AUTO-PRIME→read learnings\n"
|
|
161
|
-
"- **Guard:** `nexo_guard_check(files='...', area='...')` BEFORE editing code. "
|
|
162
|
-
"Blocking rules→resolve first. `nexo_track(sid=SID, paths=[...])` before shared files\n"
|
|
163
169
|
"- **Followups:** NEXO tasks, execute silently. 'done'/'all set'→`nexo_followup_complete` NOW. "
|
|
164
170
|
"Reminders=user's, alert when due\n"
|
|
165
171
|
"- **Observe:** correction→learning. 'tomorrow'→followup. person→entity. open topic→followup 3d\n"
|
|
@@ -175,8 +181,6 @@ mcp = FastMCP(
|
|
|
175
181
|
"write `nexo_session_diary_write(...)` with self_critique BEFORE responding. "
|
|
176
182
|
"Detect intent, not keywords. If session closes without diary, auto_close handles it.\n"
|
|
177
183
|
"- **Cortex:** `nexo_cortex_check` before budget/campaign/architecture changes\n"
|
|
178
|
-
"- **Skills:** before multi-step tasks, `nexo_skill_match(task)` to find reusable procedures. "
|
|
179
|
-
"If match found, read it and follow the steps. After completion, `nexo_skill_result(id, success, context)` to record outcome.\n"
|
|
180
184
|
"- **Dissonance:** user contradicts memory→`nexo_cognitive_dissonance`. Frustrated→force=True\n"
|
|
181
185
|
"- **Trust:** <40=paranoid verify twice, >80=fluid. Check: `nexo_cognitive_trust`"
|
|
182
186
|
),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Run Runtime Doctor
|
|
2
|
+
|
|
3
|
+
Use this skill when you want a fast health snapshot of the running NEXO system.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
1. Run the runtime doctor for the requested tier.
|
|
7
|
+
2. Review the degraded or critical checks first.
|
|
8
|
+
3. If the report recommends deterministic fixes, decide whether to run them explicitly.
|
|
9
|
+
|
|
10
|
+
## Gotchas
|
|
11
|
+
- A critical watchdog result reflects a real system issue, not just a stale skill.
|
|
12
|
+
- `all` is broader and slower than `runtime`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main() -> int:
|
|
8
|
+
tier = sys.argv[1] if len(sys.argv) > 1 and sys.argv[1] else "runtime"
|
|
9
|
+
nexo_code = os.environ.get("NEXO_CODE", "")
|
|
10
|
+
if not nexo_code:
|
|
11
|
+
print("NEXO_CODE not set", file=sys.stderr)
|
|
12
|
+
return 1
|
|
13
|
+
|
|
14
|
+
cli_py = os.path.join(nexo_code, "cli.py")
|
|
15
|
+
cmd = [sys.executable, cli_py, "doctor", "--tier", tier, "--json"]
|
|
16
|
+
result = subprocess.run(cmd, text=True)
|
|
17
|
+
return result.returncode
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "SK-RUN-RUNTIME-DOCTOR",
|
|
3
|
+
"name": "Run Runtime Doctor",
|
|
4
|
+
"description": "Runs the NEXO runtime doctor and returns the current health report.",
|
|
5
|
+
"level": "published",
|
|
6
|
+
"mode": "execute",
|
|
7
|
+
"source_kind": "core",
|
|
8
|
+
"execution_level": "read-only",
|
|
9
|
+
"approval_required": false,
|
|
10
|
+
"tags": ["doctor", "diagnostics", "runtime"],
|
|
11
|
+
"trigger_patterns": ["run doctor", "check runtime health", "diagnose nexo"],
|
|
12
|
+
"params_schema": {
|
|
13
|
+
"tier": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"required": false,
|
|
16
|
+
"default": "runtime",
|
|
17
|
+
"enum": ["boot", "runtime", "deep", "all"]
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"command_template": {
|
|
21
|
+
"argv": ["{{file_path}}", "{{tier}}"]
|
|
22
|
+
},
|
|
23
|
+
"executable_entry": "script.py",
|
|
24
|
+
"stable_after_uses": 10
|
|
25
|
+
}
|