nexo-brain 2.4.0 → 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 +65 -2
- package/bin/nexo-brain.js +208 -11
- 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 +700 -35
- 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 -654
- 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 +383 -578
- 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 -346
- package/src/dashboard/templates/memory.html +317 -197
- package/src/dashboard/templates/operations.html +521 -698
- 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 -182
- 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 +16 -1
- package/src/db/_sessions.py +22 -0
- package/src/db/_skills.py +980 -274
- 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/post-compact.sh +5 -1
- package/src/hooks/pre-compact.sh +1 -1
- 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 -48
- 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
package/src/hooks/pre-compact.sh
CHANGED
|
@@ -22,7 +22,7 @@ if [ -f "$NEXO_DB" ]; then
|
|
|
22
22
|
SELECT sid FROM sessions ORDER BY last_update_epoch DESC LIMIT 1
|
|
23
23
|
" 2>/dev/null || echo "")
|
|
24
24
|
|
|
25
|
-
if [ -n "$LATEST_SID" ]; then
|
|
25
|
+
if [ -n "$LATEST_SID" ] && [[ "$LATEST_SID" =~ ^nexo-[0-9]+-[0-9]+$ ]]; then
|
|
26
26
|
# Write SID to temp file so PostCompact knows which session compacted
|
|
27
27
|
echo "$LATEST_SID" > /tmp/nexo-compacting-sid
|
|
28
28
|
# Pull diary draft data into checkpoint
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Doctor plugin — exposes nexo_doctor as MCP tool."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Ensure src/ is importable
|
|
9
|
+
SRC_DIR = Path(__file__).resolve().parent.parent
|
|
10
|
+
if str(SRC_DIR) not in sys.path:
|
|
11
|
+
sys.path.insert(0, str(SRC_DIR))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def handle_doctor(tier: str = "boot", fix: bool = False, output: str = "text") -> str:
|
|
15
|
+
"""Unified diagnostic report for boot/runtime/deep health.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
tier: Diagnostic tier — boot, runtime, deep, or all (default: boot)
|
|
19
|
+
fix: Apply deterministic fixes (default: False)
|
|
20
|
+
output: Output format — text or json (default: text)
|
|
21
|
+
"""
|
|
22
|
+
from doctor.orchestrator import run_doctor
|
|
23
|
+
from doctor.formatters import format_report
|
|
24
|
+
|
|
25
|
+
if tier not in ("boot", "runtime", "deep", "all"):
|
|
26
|
+
return f"Invalid tier '{tier}'. Use: boot, runtime, deep, all"
|
|
27
|
+
if output not in ("text", "json"):
|
|
28
|
+
return f"Invalid output '{output}'. Use: text, json"
|
|
29
|
+
|
|
30
|
+
report = run_doctor(tier=tier, fix=fix)
|
|
31
|
+
return format_report(report, fmt=output)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
TOOLS = [
|
|
35
|
+
(handle_doctor, "nexo_doctor", "Unified diagnostic report for boot/runtime/deep health."),
|
|
36
|
+
]
|
package/src/plugins/evolution.py
CHANGED
|
@@ -18,7 +18,8 @@ def handle_evolution_status() -> str:
|
|
|
18
18
|
"agi": "AGI",
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
from user_context import get_context
|
|
22
|
+
lines = [f"{get_context().assistant_name} EVOLUTION STATUS:"]
|
|
22
23
|
for key, label in BARS.items():
|
|
23
24
|
m = metrics.get(key)
|
|
24
25
|
if m:
|
package/src/plugins/skills.py
CHANGED
|
@@ -1,264 +1,224 @@
|
|
|
1
|
-
"""Skills plugin — reusable procedures
|
|
1
|
+
"""Skills plugin — reusable procedures, executable skills, and feedback loops."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
declarative (don't do X). Created automatically by Deep Sleep or manually.
|
|
5
|
-
|
|
6
|
-
Pipeline: trace → draft → published, fully autonomous.
|
|
7
|
-
Trust score with decay controls quality — no human approval gates.
|
|
8
|
-
"""
|
|
3
|
+
from __future__ import annotations
|
|
9
4
|
|
|
10
5
|
import json
|
|
6
|
+
|
|
11
7
|
from db import (
|
|
12
|
-
create_skill,
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
create_skill,
|
|
9
|
+
delete_skill,
|
|
10
|
+
get_skill,
|
|
11
|
+
get_skill_stats,
|
|
12
|
+
list_skills,
|
|
13
|
+
match_skills,
|
|
14
|
+
merge_skills,
|
|
15
|
+
record_skill_usage,
|
|
16
|
+
search_skills,
|
|
17
|
+
update_skill,
|
|
18
|
+
)
|
|
19
|
+
from skills_runtime import (
|
|
20
|
+
apply_skill,
|
|
21
|
+
approve_skill_execution,
|
|
22
|
+
get_featured_skill_summaries,
|
|
23
|
+
list_evolution_candidates,
|
|
24
|
+
sync_skills,
|
|
15
25
|
)
|
|
16
26
|
|
|
17
27
|
|
|
18
28
|
def handle_skill_create(
|
|
19
29
|
id: str,
|
|
20
30
|
name: str,
|
|
21
|
-
description: str =
|
|
22
|
-
level: str =
|
|
23
|
-
tags: str =
|
|
24
|
-
trigger_patterns: str =
|
|
25
|
-
source_sessions: str =
|
|
26
|
-
linked_learnings: str =
|
|
27
|
-
file_path: str =
|
|
31
|
+
description: str = "",
|
|
32
|
+
level: str = "draft",
|
|
33
|
+
tags: str = "[]",
|
|
34
|
+
trigger_patterns: str = "[]",
|
|
35
|
+
source_sessions: str = "[]",
|
|
36
|
+
linked_learnings: str = "[]",
|
|
37
|
+
file_path: str = "",
|
|
38
|
+
mode: str = "",
|
|
39
|
+
source_kind: str = "personal",
|
|
40
|
+
execution_level: str = "none",
|
|
41
|
+
approval_required: bool = False,
|
|
42
|
+
params_schema: str = "{}",
|
|
43
|
+
command_template: str = "{}",
|
|
44
|
+
executable_entry: str = "",
|
|
28
45
|
) -> str:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Skills are procedural knowledge — step-by-step instructions for complex tasks.
|
|
32
|
-
Created by Deep Sleep (auto-extraction) or manually during sessions.
|
|
33
|
-
|
|
34
|
-
Pipeline levels: trace → draft → published → archived.
|
|
35
|
-
Promotion is automatic: 2+ successful uses in distinct contexts → published.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
id: Unique ID starting with 'SK-' (e.g., SK-DEPLOY-CHROME-EXT).
|
|
39
|
-
name: Human-readable name (e.g., 'Deploy Chrome Extension').
|
|
40
|
-
description: What this skill does (1-2 sentences).
|
|
41
|
-
level: Starting level — trace, draft (default), published, archived.
|
|
42
|
-
tags: JSON array of tags for discovery (e.g., '["chrome", "extension", "deploy"]').
|
|
43
|
-
trigger_patterns: JSON array of phrases that should trigger this skill
|
|
44
|
-
(e.g., '["deploy extension", "publish chrome"]').
|
|
45
|
-
source_sessions: JSON array of diary IDs where this skill was observed.
|
|
46
|
-
linked_learnings: JSON array of learning IDs related to this skill.
|
|
47
|
-
file_path: Path to the .md file with full procedure (if stored as file).
|
|
48
|
-
"""
|
|
49
|
-
if not id.startswith('SK-'):
|
|
46
|
+
if not id.startswith("SK-"):
|
|
50
47
|
return "ERROR: Skill ID must start with 'SK-' (e.g., SK-DEPLOY-CHROME-EXT)"
|
|
51
|
-
|
|
52
|
-
existing = get_skill(id)
|
|
53
|
-
if existing:
|
|
48
|
+
if get_skill(id):
|
|
54
49
|
return f"ERROR: Skill {id} already exists. Use nexo_skill_update to modify."
|
|
55
50
|
|
|
56
51
|
result = create_skill(
|
|
57
|
-
skill_id=id,
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
skill_id=id,
|
|
53
|
+
name=name,
|
|
54
|
+
description=description,
|
|
55
|
+
level=level,
|
|
56
|
+
tags=tags,
|
|
57
|
+
trigger_patterns=trigger_patterns,
|
|
58
|
+
source_sessions=source_sessions,
|
|
59
|
+
linked_learnings=linked_learnings,
|
|
60
60
|
file_path=file_path,
|
|
61
|
+
mode=mode,
|
|
62
|
+
source_kind=source_kind,
|
|
63
|
+
execution_level=execution_level,
|
|
64
|
+
approval_required=approval_required,
|
|
65
|
+
params_schema=params_schema,
|
|
66
|
+
command_template=command_template,
|
|
67
|
+
executable_entry=executable_entry,
|
|
61
68
|
)
|
|
62
69
|
if "error" in result:
|
|
63
70
|
return f"ERROR: {result['error']}"
|
|
64
71
|
|
|
65
72
|
return (
|
|
66
|
-
f"Skill {id} created ({level}, trust={result.get('trust_score', 50)}).\n"
|
|
73
|
+
f"Skill {id} created ({result['level']}, {result.get('mode', 'guide')}, trust={result.get('trust_score', 50)}).\n"
|
|
67
74
|
f" Name: {name}\n"
|
|
68
|
-
f"
|
|
69
|
-
f"
|
|
75
|
+
f" Source: {result.get('source_kind', source_kind)}\n"
|
|
76
|
+
f" Execution: {result.get('execution_level', execution_level)}"
|
|
70
77
|
)
|
|
71
78
|
|
|
72
79
|
|
|
73
|
-
def handle_skill_match(task: str, level: str =
|
|
74
|
-
"""Find skills matching a task description. Call BEFORE starting multi-step tasks.
|
|
75
|
-
|
|
76
|
-
Searches by: FTS5 relevance, trigger pattern matching, tag keyword overlap.
|
|
77
|
-
Returns top-3 matches sorted by trust score.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
task: Description of what you're about to do (e.g., 'deploy chrome extension to CWS').
|
|
81
|
-
level: Filter by level (optional). Default: draft + published.
|
|
82
|
-
"""
|
|
80
|
+
def handle_skill_match(task: str, level: str = "") -> str:
|
|
83
81
|
matches = match_skills(task, level=level)
|
|
84
82
|
if not matches:
|
|
85
83
|
return f"No skills found for: '{task}'"
|
|
86
84
|
|
|
87
85
|
lines = [f"SKILLS MATCHED ({len(matches)}) for '{task}':"]
|
|
88
|
-
for
|
|
89
|
-
match_method =
|
|
90
|
-
fp = f" → {m['file_path']}" if m.get('file_path') else ""
|
|
86
|
+
for match in matches:
|
|
87
|
+
match_method = match.pop("_match", "unknown")
|
|
91
88
|
lines.append(
|
|
92
|
-
f" [{
|
|
93
|
-
f"used={
|
|
94
|
-
f"
|
|
89
|
+
f" [{match['id']}] {match['name']} ({match['level']}, {match.get('mode', 'guide')}, "
|
|
90
|
+
f"{match.get('source_kind', 'personal')}, trust={match['trust_score']}, used={match['use_count']}x) "
|
|
91
|
+
f"via {match_method}"
|
|
95
92
|
)
|
|
96
|
-
|
|
97
|
-
triggers = json.loads(m.get('trigger_patterns', '[]'))
|
|
98
|
-
if triggers:
|
|
99
|
-
lines.append(f" Triggers: {', '.join(triggers[:5])}")
|
|
100
|
-
except (json.JSONDecodeError, TypeError):
|
|
101
|
-
pass
|
|
93
|
+
lines.append(f" {match['description'][:140]}")
|
|
102
94
|
return "\n".join(lines)
|
|
103
95
|
|
|
104
96
|
|
|
105
97
|
def handle_skill_get(id: str) -> str:
|
|
106
|
-
"""Get a skill's full details including usage history.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
id: Skill ID (e.g., SK-DEPLOY-CHROME-EXT).
|
|
110
|
-
"""
|
|
111
98
|
skill = get_skill(id)
|
|
112
99
|
if not skill:
|
|
113
100
|
return f"ERROR: Skill {id} not found."
|
|
114
101
|
|
|
115
|
-
from db import get_db
|
|
116
|
-
conn = get_db()
|
|
117
|
-
recent_uses = conn.execute(
|
|
118
|
-
"SELECT * FROM skill_usage WHERE skill_id = ? ORDER BY created_at DESC LIMIT 5",
|
|
119
|
-
(id,),
|
|
120
|
-
).fetchall()
|
|
121
|
-
|
|
122
102
|
lines = [
|
|
123
103
|
f"SKILL: {skill['id']}",
|
|
124
104
|
f" Name: {skill['name']}",
|
|
125
105
|
f" Description: {skill['description']}",
|
|
126
106
|
f" Level: {skill['level']}",
|
|
107
|
+
f" Mode: {skill.get('mode', 'guide')}",
|
|
108
|
+
f" Source: {skill.get('source_kind', 'personal')}",
|
|
127
109
|
f" Trust: {skill['trust_score']}",
|
|
128
|
-
f"
|
|
129
|
-
f"
|
|
110
|
+
f" Execution level: {skill.get('execution_level', 'none')}",
|
|
111
|
+
f" Approval required: {bool(skill.get('approval_required', 0))}",
|
|
112
|
+
f" Approved at: {skill.get('approved_at') or 'no'}",
|
|
113
|
+
f" Definition: {skill.get('definition_path') or '(none)'}",
|
|
114
|
+
f" File: {skill.get('file_path') or '(none)'}",
|
|
115
|
+
f" Params schema: {skill.get('params_schema', '{}')}",
|
|
130
116
|
f" Triggers: {skill['trigger_patterns']}",
|
|
131
|
-
f" Source sessions: {skill['source_sessions']}",
|
|
132
|
-
f" Linked learnings: {skill['linked_learnings']}",
|
|
133
117
|
f" Stats: {skill['use_count']} uses, {skill['success_count']} success, {skill['fail_count']} fail",
|
|
134
|
-
f" Created: {skill['created_at']}",
|
|
135
|
-
f" Last used: {skill['last_used_at'] or 'never'}",
|
|
136
118
|
]
|
|
137
|
-
|
|
138
|
-
if recent_uses:
|
|
139
|
-
lines.append("\n RECENT USAGE:")
|
|
140
|
-
for u in recent_uses:
|
|
141
|
-
u = dict(u)
|
|
142
|
-
status = "✓" if u['success'] else "✗"
|
|
143
|
-
lines.append(f" {status} {u['created_at']} — {u['context'][:60] or '(no context)'}")
|
|
144
|
-
if u.get('notes'):
|
|
145
|
-
lines.append(f" Notes: {u['notes'][:80]}")
|
|
146
|
-
|
|
147
119
|
return "\n".join(lines)
|
|
148
120
|
|
|
149
121
|
|
|
150
|
-
def handle_skill_result(id: str, success: bool = True, context: str =
|
|
151
|
-
"""Record the result of using a skill. Auto-promotes/degrades based on trust rules.
|
|
152
|
-
|
|
153
|
-
Call this AFTER following a skill's procedure to record whether it worked.
|
|
154
|
-
- Success: trust +5. After 2+ successes in distinct contexts: draft → published.
|
|
155
|
-
- Failure: trust -10. If trust < 20: → archived.
|
|
156
|
-
|
|
157
|
-
Args:
|
|
158
|
-
id: Skill ID.
|
|
159
|
-
success: Whether the skill's procedure worked correctly.
|
|
160
|
-
context: What task you were doing (used for distinct-context promotion).
|
|
161
|
-
notes: Additional notes (especially useful for failures — what went wrong).
|
|
162
|
-
"""
|
|
122
|
+
def handle_skill_result(id: str, success: bool = True, context: str = "", notes: str = "") -> str:
|
|
163
123
|
result = record_skill_usage(skill_id=id, success=success, context=context, notes=notes)
|
|
164
124
|
if "error" in result:
|
|
165
125
|
return f"ERROR: {result['error']}"
|
|
166
126
|
|
|
167
|
-
promotion = result.
|
|
168
|
-
|
|
169
|
-
msg = f"Skill {id} usage recorded: {status} (trust={result['trust_score']})"
|
|
127
|
+
promotion = result.get("_promotion")
|
|
128
|
+
msg = f"Skill {id} usage recorded: {'SUCCESS' if success else 'FAILURE'} (trust={result['trust_score']})"
|
|
170
129
|
if promotion:
|
|
171
130
|
msg += f"\n ⚡ PROMOTION: {promotion}"
|
|
172
131
|
return msg
|
|
173
132
|
|
|
174
133
|
|
|
175
|
-
def handle_skill_list(level: str =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
level: Filter by level — trace, draft, published, archived.
|
|
180
|
-
tag: Filter by tag (e.g., 'chrome', 'deploy', 'shopify').
|
|
181
|
-
"""
|
|
182
|
-
skills = list_skills(level=level, tag=tag)
|
|
134
|
+
def handle_skill_list(level: str = "", tag: str = "", source_kind: str = "") -> str:
|
|
135
|
+
skills = list_skills(level=level, tag=tag, source_kind=source_kind)
|
|
183
136
|
if not skills:
|
|
184
|
-
|
|
185
|
-
if level: filters.append(f"level={level}")
|
|
186
|
-
if tag: filters.append(f"tag={tag}")
|
|
187
|
-
return f"No skills found{' (' + ', '.join(filters) + ')' if filters else ''}."
|
|
137
|
+
return "No skills found."
|
|
188
138
|
|
|
189
139
|
lines = [f"SKILLS ({len(skills)}):"]
|
|
190
|
-
for
|
|
191
|
-
fp = f" → {s['file_path']}" if s.get('file_path') else ""
|
|
192
|
-
used = f", last={s['last_used_at'][:10]}" if s.get('last_used_at') else ""
|
|
140
|
+
for skill in skills:
|
|
193
141
|
lines.append(
|
|
194
|
-
f" [{
|
|
195
|
-
f"used={
|
|
142
|
+
f" [{skill['id']}] {skill['name']} ({skill['level']}, {skill.get('mode', 'guide')}, "
|
|
143
|
+
f"{skill.get('source_kind', 'personal')}, trust={skill['trust_score']}, used={skill['use_count']}x)"
|
|
196
144
|
)
|
|
197
145
|
return "\n".join(lines)
|
|
198
146
|
|
|
199
147
|
|
|
200
|
-
def handle_skill_merge(id1: str, id2: str, keep_id: str =
|
|
201
|
-
"""Merge two similar skills into one. Combines tags, triggers, usage history.
|
|
202
|
-
|
|
203
|
-
The survivor keeps the higher trust score and all combined metadata.
|
|
204
|
-
The donor is deleted.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
id1: First skill ID.
|
|
208
|
-
id2: Second skill ID.
|
|
209
|
-
keep_id: Which one to keep (default: higher trust score).
|
|
210
|
-
"""
|
|
148
|
+
def handle_skill_merge(id1: str, id2: str, keep_id: str = "") -> str:
|
|
211
149
|
result = merge_skills(id1, id2, keep_id=keep_id)
|
|
212
150
|
if "error" in result:
|
|
213
151
|
return f"ERROR: {result['error']}"
|
|
214
|
-
|
|
215
|
-
merged_from = result.pop('_merged_from', '?')
|
|
216
152
|
return (
|
|
217
|
-
f"Skills merged. Kept {result['id']}, deleted {
|
|
218
|
-
f" Trust: {result['trust_score']}, Uses: {result['use_count']}
|
|
219
|
-
f"Tags: {result['tags']}"
|
|
153
|
+
f"Skills merged. Kept {result['id']}, deleted {result['_merged_from']}.\n"
|
|
154
|
+
f" Trust: {result['trust_score']}, Uses: {result['use_count']}"
|
|
220
155
|
)
|
|
221
156
|
|
|
222
157
|
|
|
223
158
|
def handle_skill_stats() -> str:
|
|
224
|
-
"""Show aggregate skill statistics: total count, by level, avg trust, usage rates."""
|
|
225
159
|
stats = get_skill_stats()
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
"
|
|
229
|
-
f"
|
|
230
|
-
f"
|
|
231
|
-
f"
|
|
232
|
-
f"
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
160
|
+
return (
|
|
161
|
+
"SKILL STATS:\n"
|
|
162
|
+
f" Total: {stats['total']}\n"
|
|
163
|
+
f" By level: {', '.join(f'{k}={v}' for k, v in sorted(stats['by_level'].items()))}\n"
|
|
164
|
+
f" Avg trust: {stats['avg_trust']}\n"
|
|
165
|
+
f" Total uses: {stats['total_uses']} (success rate: {stats['success_rate']}%)\n"
|
|
166
|
+
f" Uses last 7d: {stats['uses_last_7d']}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def handle_skill_apply(id: str, params: str = "{}", mode: str = "auto", dry_run: bool = False, context: str = "") -> str:
|
|
171
|
+
return json.dumps(apply_skill(id, params=params, mode=mode, dry_run=dry_run, context=context), ensure_ascii=False)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def handle_skill_approve(id: str, execution_level: str = "", approved_by: str = "") -> str:
|
|
175
|
+
result = approve_skill_execution(id, execution_level=execution_level, approved_by=approved_by)
|
|
176
|
+
if "error" in result:
|
|
177
|
+
return f"ERROR: {result['error']}"
|
|
178
|
+
return (
|
|
179
|
+
f"Skill {id} approved.\n"
|
|
180
|
+
f" Execution level: {result.get('execution_level', 'none')}\n"
|
|
181
|
+
f" Approved at: {result.get('approved_at', '')}\n"
|
|
182
|
+
f" Approved by: {result.get('approved_by', '')}"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def handle_skill_sync() -> str:
|
|
187
|
+
result = sync_skills()
|
|
188
|
+
return json.dumps(result, ensure_ascii=False)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def handle_skill_featured(limit: int = 5) -> str:
|
|
192
|
+
return json.dumps(get_featured_skill_summaries(limit=limit), ensure_ascii=False)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def handle_skill_evolution_candidates() -> str:
|
|
196
|
+
return json.dumps(list_evolution_candidates(), ensure_ascii=False)
|
|
236
197
|
|
|
237
198
|
|
|
238
|
-
# Plugin registration — TOOLS array consumed by plugin_loader.py
|
|
239
199
|
TOOLS = [
|
|
240
200
|
(handle_skill_create, "nexo_skill_create",
|
|
241
|
-
"Create a new skill
|
|
242
|
-
"Auto-promoted from draft→published after 2+ successful uses. ID must start with 'SK-'."),
|
|
243
|
-
|
|
201
|
+
"Create a new skill with guide/execute/hybrid metadata, triggers, params schema, and execution level."),
|
|
244
202
|
(handle_skill_match, "nexo_skill_match",
|
|
245
|
-
"Find skills matching a task description. Call
|
|
246
|
-
"to check if a reusable procedure exists. Returns top-3 matches by trust score."),
|
|
247
|
-
|
|
203
|
+
"Find skills matching a task description. Call before multi-step tasks."),
|
|
248
204
|
(handle_skill_get, "nexo_skill_get",
|
|
249
|
-
"Get a skill's full details including
|
|
250
|
-
|
|
205
|
+
"Get a skill's full details, including execution metadata and approval state."),
|
|
251
206
|
(handle_skill_result, "nexo_skill_result",
|
|
252
|
-
"Record the result of using a skill
|
|
253
|
-
"after 2+ successes, auto-archives if trust drops below 20."),
|
|
254
|
-
|
|
207
|
+
"Record the result of using a skill. Updates trust and promotions."),
|
|
255
208
|
(handle_skill_list, "nexo_skill_list",
|
|
256
|
-
"List
|
|
257
|
-
|
|
209
|
+
"List skills, optionally filtered by level, tag, or source kind."),
|
|
258
210
|
(handle_skill_merge, "nexo_skill_merge",
|
|
259
|
-
"Merge two similar skills into one.
|
|
260
|
-
"Survivor keeps the higher trust score."),
|
|
261
|
-
|
|
211
|
+
"Merge two similar skills into one."),
|
|
262
212
|
(handle_skill_stats, "nexo_skill_stats",
|
|
263
|
-
"Show aggregate skill statistics
|
|
213
|
+
"Show aggregate skill statistics."),
|
|
214
|
+
(handle_skill_apply, "nexo_skill_apply",
|
|
215
|
+
"Apply a skill in guide, execute, or hybrid mode. Execution goes through the stable nexo scripts runtime."),
|
|
216
|
+
(handle_skill_approve, "nexo_skill_approve",
|
|
217
|
+
"Approve a local/remote executable skill so it can run."),
|
|
218
|
+
(handle_skill_sync, "nexo_skill_sync",
|
|
219
|
+
"Sync filesystem skill definitions from personal/core/community directories into SQLite."),
|
|
220
|
+
(handle_skill_featured, "nexo_skill_featured",
|
|
221
|
+
"Return featured published/stable skills for startup discovery."),
|
|
222
|
+
(handle_skill_evolution_candidates, "nexo_skill_evolution_candidates",
|
|
223
|
+
"Return candidates for skill improvement or text-to-script evolution."),
|
|
264
224
|
]
|
package/src/requirements.txt
CHANGED