superlocalmemory 3.4.9 → 3.4.10
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 +14 -0
- package/docs/cloud-backup.md +174 -0
- package/docs/skill-evolution.md +189 -0
- package/ide/hooks/tool-event-hook.sh +101 -11
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/superlocalmemory/cli/commands.py +189 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +81 -29
- package/src/superlocalmemory/cli/main.py +11 -0
- package/src/superlocalmemory/core/consolidation_engine.py +10 -0
- package/src/superlocalmemory/core/engine.py +7 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +24 -3
- package/src/superlocalmemory/encoding/entity_resolver.py +95 -28
- package/src/superlocalmemory/infra/backup.py +63 -20
- package/src/superlocalmemory/infra/cloud_backup.py +703 -0
- package/src/superlocalmemory/learning/skill_performance_miner.py +389 -0
- package/src/superlocalmemory/server/routes/backup.py +512 -8
- package/src/superlocalmemory/server/routes/behavioral.py +23 -5
- package/src/superlocalmemory/storage/schema_v3410.py +159 -0
- package/src/superlocalmemory/ui/index.html +55 -2
- package/src/superlocalmemory/ui/js/core.js +3 -0
- package/src/superlocalmemory/ui/js/ng-entities.js +27 -3
- package/src/superlocalmemory/ui/js/ng-shell.js +33 -0
- package/src/superlocalmemory/ui/js/ng-skills.js +227 -0
- package/src/superlocalmemory/ui/js/settings.js +311 -1
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -594
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -317
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -55
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3.4.10 "Fortress" — Schema Extensions.
|
|
6
|
+
|
|
7
|
+
New tables for cloud backup + entity quality:
|
|
8
|
+
- backup_destinations: Cloud backup targets (Google Drive, GitHub, local)
|
|
9
|
+
- entity_blacklist: Stop words and known garbage entity names
|
|
10
|
+
|
|
11
|
+
Design rules (inherited):
|
|
12
|
+
- CREATE IF NOT EXISTS for idempotency
|
|
13
|
+
- profile_id where applicable
|
|
14
|
+
- Never ALTER existing column types
|
|
15
|
+
|
|
16
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import sqlite3
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# DDL — Backup Destinations
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
_BACKUP_DESTINATIONS_DDL = """
|
|
31
|
+
CREATE TABLE IF NOT EXISTS backup_destinations (
|
|
32
|
+
id TEXT PRIMARY KEY,
|
|
33
|
+
profile_id TEXT DEFAULT 'default',
|
|
34
|
+
destination_type TEXT NOT NULL,
|
|
35
|
+
display_name TEXT DEFAULT '',
|
|
36
|
+
credentials_ref TEXT DEFAULT '',
|
|
37
|
+
config TEXT DEFAULT '{}',
|
|
38
|
+
last_sync_at TEXT,
|
|
39
|
+
last_sync_status TEXT DEFAULT 'never',
|
|
40
|
+
last_sync_error TEXT DEFAULT '',
|
|
41
|
+
enabled INTEGER DEFAULT 1,
|
|
42
|
+
created_at TEXT NOT NULL
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_backup_dest_type
|
|
46
|
+
ON backup_destinations(destination_type);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_backup_dest_profile
|
|
48
|
+
ON backup_destinations(profile_id);
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# DDL — Entity Blacklist
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
_ENTITY_BLACKLIST_DDL = """
|
|
56
|
+
CREATE TABLE IF NOT EXISTS entity_blacklist (
|
|
57
|
+
term TEXT PRIMARY KEY,
|
|
58
|
+
reason TEXT DEFAULT 'stop_word',
|
|
59
|
+
added_at TEXT NOT NULL
|
|
60
|
+
);
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Default blacklist entries (seeded on first migration)
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
_DEFAULT_BLACKLIST = [
|
|
68
|
+
# English stop words that frequently become garbage entities
|
|
69
|
+
"a", "an", "the", "all", "not", "no", "yes", "and", "or", "but",
|
|
70
|
+
"if", "is", "are", "was", "were", "be", "been", "being",
|
|
71
|
+
"have", "has", "had", "do", "does", "did", "will", "would",
|
|
72
|
+
"shall", "should", "can", "could", "just", "also", "only",
|
|
73
|
+
"very", "too", "so", "then", "than", "that", "this",
|
|
74
|
+
"each", "every", "both", "few", "more", "most", "other",
|
|
75
|
+
"some", "such", "any", "many", "much", "own", "same",
|
|
76
|
+
"new", "old", "first", "last", "next", "now",
|
|
77
|
+
# Months (biggest historical source of garbage)
|
|
78
|
+
"january", "february", "march", "april", "may", "june",
|
|
79
|
+
"july", "august", "september", "october", "november", "december",
|
|
80
|
+
# Technical terms commonly misclassified
|
|
81
|
+
"test", "fix", "build", "check", "run", "start", "stop",
|
|
82
|
+
"error", "status", "version", "query", "data", "file",
|
|
83
|
+
"ready", "done", "complete", "pending", "active", "failed",
|
|
84
|
+
"total", "count", "key", "value", "true", "false",
|
|
85
|
+
# Abstract nouns misclassified as people
|
|
86
|
+
"completeness", "correctness", "limitations", "requirements",
|
|
87
|
+
"performance", "security", "quality", "coverage", "progress",
|
|
88
|
+
"analysis", "research", "implementation", "verification",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
# Migration runner
|
|
94
|
+
# ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
def apply_v3410_schema(db_path: str | sqlite3.Connection) -> dict:
|
|
97
|
+
"""Apply all v3.4.10 schema changes. Idempotent."""
|
|
98
|
+
result = {"applied": [], "errors": []}
|
|
99
|
+
|
|
100
|
+
if isinstance(db_path, sqlite3.Connection):
|
|
101
|
+
conn = db_path
|
|
102
|
+
own_connection = False
|
|
103
|
+
else:
|
|
104
|
+
conn = sqlite3.connect(str(db_path))
|
|
105
|
+
own_connection = True
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Backup destinations table
|
|
109
|
+
try:
|
|
110
|
+
conn.executescript(_BACKUP_DESTINATIONS_DDL)
|
|
111
|
+
result["applied"].append("backup_destinations table + indexes")
|
|
112
|
+
except sqlite3.OperationalError as e:
|
|
113
|
+
result["errors"].append(f"backup_destinations: {e}")
|
|
114
|
+
|
|
115
|
+
# Entity blacklist table
|
|
116
|
+
try:
|
|
117
|
+
conn.executescript(_ENTITY_BLACKLIST_DDL)
|
|
118
|
+
result["applied"].append("entity_blacklist table")
|
|
119
|
+
except sqlite3.OperationalError as e:
|
|
120
|
+
result["errors"].append(f"entity_blacklist: {e}")
|
|
121
|
+
|
|
122
|
+
# Seed default blacklist (only inserts missing entries)
|
|
123
|
+
now = __import__("datetime").datetime.now().isoformat()
|
|
124
|
+
seeded = 0
|
|
125
|
+
for term in _DEFAULT_BLACKLIST:
|
|
126
|
+
try:
|
|
127
|
+
conn.execute(
|
|
128
|
+
"INSERT OR IGNORE INTO entity_blacklist (term, reason, added_at) "
|
|
129
|
+
"VALUES (?, 'stop_word', ?)",
|
|
130
|
+
(term, now),
|
|
131
|
+
)
|
|
132
|
+
seeded += 1
|
|
133
|
+
except sqlite3.OperationalError:
|
|
134
|
+
pass
|
|
135
|
+
if seeded:
|
|
136
|
+
result["applied"].append(f"entity_blacklist seeded ({len(_DEFAULT_BLACKLIST)} terms)")
|
|
137
|
+
|
|
138
|
+
# Mark version
|
|
139
|
+
try:
|
|
140
|
+
conn.execute(
|
|
141
|
+
"INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)",
|
|
142
|
+
("3.4.10", now),
|
|
143
|
+
)
|
|
144
|
+
except sqlite3.OperationalError:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
conn.commit()
|
|
148
|
+
|
|
149
|
+
if result["applied"]:
|
|
150
|
+
logger.info("Schema v3.4.10 applied: %s", ", ".join(result["applied"]))
|
|
151
|
+
|
|
152
|
+
except Exception as e:
|
|
153
|
+
result["errors"].append(f"fatal: {e}")
|
|
154
|
+
logger.error("Schema v3.4.10 migration failed: %s", e)
|
|
155
|
+
finally:
|
|
156
|
+
if own_connection:
|
|
157
|
+
conn.close()
|
|
158
|
+
|
|
159
|
+
return result
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/* Cloud account widget — sidebar (v3.4.10) */
|
|
47
|
+
#ng-account-widget:hover { background: rgba(255,255,255,0.06) !important; }
|
|
48
|
+
.account-dest-item { padding: 6px 0; display: flex; align-items: center; gap: 8px; }
|
|
49
|
+
.account-dest-badge { font-size: 10px; padding: 1px 6px; border-radius: 4px; }
|
|
50
|
+
.account-dest-badge.synced { background: rgba(0,212,170,0.15); color: #00D4AA; }
|
|
51
|
+
.account-dest-badge.failed { background: rgba(255,71,87,0.15); color: #ff4757; }
|
|
52
|
+
.account-dest-badge.never { background: rgba(255,255,255,0.06); color: #888; }
|
|
53
|
+
|
|
46
54
|
/* Stat cards with gradient backgrounds */
|
|
47
55
|
.stat-card {
|
|
48
56
|
border: none;
|
|
@@ -1717,6 +1725,25 @@
|
|
|
1717
1725
|
<div id="entity-detail-panel" style="display:none;margin-top:24px"></div>
|
|
1718
1726
|
</div>
|
|
1719
1727
|
|
|
1728
|
+
<!-- Skill Evolution (v3.4.10 — Arsenal Evolution) -->
|
|
1729
|
+
<div class="tab-pane fade" id="skills-pane">
|
|
1730
|
+
<div class="ng-content-header">
|
|
1731
|
+
<div>
|
|
1732
|
+
<div class="ng-content-title"><i class="bi bi-lightning-charge"></i> Skill Evolution</div>
|
|
1733
|
+
<div class="ng-content-subtitle">Track skill performance, evolution history, and health status</div>
|
|
1734
|
+
</div>
|
|
1735
|
+
<button class="ng-btn" onclick="loadSkillEvolution()"><i class="bi bi-arrow-clockwise"></i> Refresh</button>
|
|
1736
|
+
</div>
|
|
1737
|
+
<div id="skills-overview-cards"></div>
|
|
1738
|
+
<div id="skills-list" style="margin-top:24px">
|
|
1739
|
+
<div class="text-center" style="padding:24px;color:var(--ng-text-tertiary)">
|
|
1740
|
+
<div class="spinner-border" style="color:var(--ng-accent)"></div>
|
|
1741
|
+
<div style="margin-top:8px">Loading skill performance data...</div>
|
|
1742
|
+
</div>
|
|
1743
|
+
</div>
|
|
1744
|
+
<div id="skill-detail-panel" style="display:none;margin-top:24px"></div>
|
|
1745
|
+
</div>
|
|
1746
|
+
|
|
1720
1747
|
<!-- Mesh Peers (v3.4.3 — Neural Glass) -->
|
|
1721
1748
|
<div class="tab-pane fade" id="mesh-pane">
|
|
1722
1749
|
<div class="ng-content-header">
|
|
@@ -1974,6 +2001,31 @@
|
|
|
1974
2001
|
</div>
|
|
1975
2002
|
</div>
|
|
1976
2003
|
</div>
|
|
2004
|
+
<!-- Cloud Backup (v3.4.10 "Fortress") -->
|
|
2005
|
+
<div class="card p-3 mb-3">
|
|
2006
|
+
<h5 class="mb-3"><i class="bi bi-cloud-arrow-up"></i> Cloud Backup</h5>
|
|
2007
|
+
<p class="text-muted small mb-2">
|
|
2008
|
+
Sync your memory backups to Google Drive or GitHub. Credentials are stored securely in your OS keychain.
|
|
2009
|
+
</p>
|
|
2010
|
+
<div id="cloud-destinations" class="mb-3">
|
|
2011
|
+
<div class="text-muted small">Loading cloud destinations...</div>
|
|
2012
|
+
</div>
|
|
2013
|
+
<div class="d-flex gap-2 flex-wrap">
|
|
2014
|
+
<button class="btn btn-outline-primary btn-sm" onclick="connectGoogleDrive()">
|
|
2015
|
+
<i class="bi bi-google"></i> Connect Google Drive
|
|
2016
|
+
</button>
|
|
2017
|
+
<button class="btn btn-outline-dark btn-sm" onclick="connectGitHub()">
|
|
2018
|
+
<i class="bi bi-github"></i> Connect GitHub
|
|
2019
|
+
</button>
|
|
2020
|
+
<button class="btn btn-outline-success btn-sm" onclick="syncCloudNow()">
|
|
2021
|
+
<i class="bi bi-cloud-upload"></i> Sync Now
|
|
2022
|
+
</button>
|
|
2023
|
+
<button class="btn btn-outline-info btn-sm" onclick="exportBackup()">
|
|
2024
|
+
<i class="bi bi-file-earmark-zip"></i> Export Backup
|
|
2025
|
+
</button>
|
|
2026
|
+
</div>
|
|
2027
|
+
</div>
|
|
2028
|
+
|
|
1977
2029
|
<!-- Learning Data Management (v2.7.4) -->
|
|
1978
2030
|
<div class="card p-3 mb-3">
|
|
1979
2031
|
<h5 class="mb-3"><i class="bi bi-brain"></i> Learning Data</h5>
|
|
@@ -2097,9 +2149,10 @@
|
|
|
2097
2149
|
<!-- Neural Glass v3.4.4 — Dashboard V2 -->
|
|
2098
2150
|
<script src="static/js/ng-health.js?v=345"></script>
|
|
2099
2151
|
<script src="static/js/ng-ingestion.js?v=345"></script>
|
|
2100
|
-
<script src="static/js/ng-entities.js?v=
|
|
2152
|
+
<script src="static/js/ng-entities.js?v=3410"></script>
|
|
2153
|
+
<script src="static/js/ng-skills.js?v=3410"></script>
|
|
2101
2154
|
<script src="static/js/ng-mesh.js?v=345"></script>
|
|
2102
|
-
<script src="static/js/ng-shell.js?v=
|
|
2155
|
+
<script src="static/js/ng-shell.js?v=3410"></script>
|
|
2103
2156
|
|
|
2104
2157
|
<footer style="text-align:center; padding:24px; border-top:1px solid var(--bs-border-color); font-size:0.8125rem;">
|
|
2105
2158
|
<div style="display:flex; align-items:center; justify-content:center; gap:8px; margin-bottom:8px;">
|
|
@@ -243,4 +243,7 @@ window.addEventListener('DOMContentLoaded', function() {
|
|
|
243
243
|
if (typeof initEventStream === 'function') initEventStream();
|
|
244
244
|
if (typeof loadEventStats === 'function') loadEventStats();
|
|
245
245
|
if (typeof loadAgents === 'function') loadAgents();
|
|
246
|
+
|
|
247
|
+
// v3.4.10 — Account widget (cloud backup status in header)
|
|
248
|
+
if (typeof loadCloudDestinations === 'function') loadCloudDestinations();
|
|
246
249
|
});
|
|
@@ -85,14 +85,23 @@
|
|
|
85
85
|
var typeColor = getTypeColor(entity.type);
|
|
86
86
|
var summaryText = entity.summary_preview || 'No summary yet — click to view details';
|
|
87
87
|
var hasTruth = entity.has_compiled_truth;
|
|
88
|
+
var typeIcon = getTypeIcon(entity.type);
|
|
89
|
+
|
|
90
|
+
// Skill entities get a distinctive accent border
|
|
91
|
+
var borderAccent = entity.type === 'skill'
|
|
92
|
+
? 'border-left:3px solid ' + typeColor + ';'
|
|
93
|
+
: '';
|
|
88
94
|
|
|
89
95
|
return '<div class="col-md-6 col-lg-4">' +
|
|
90
|
-
'<div class="ng-glass" style="padding:16px;cursor:pointer;transition:border-color 0.2s" ' +
|
|
96
|
+
'<div class="ng-glass" style="padding:16px;cursor:pointer;transition:border-color 0.2s;' + borderAccent + '" ' +
|
|
91
97
|
'onclick="showEntityDetail(\'' + escapeAttr(entity.name) + '\')" ' +
|
|
92
98
|
'onmouseover="this.style.borderColor=\'var(--ng-border-prominent)\'" ' +
|
|
93
99
|
'onmouseout="this.style.borderColor=\'var(--ng-border-subtle)\'">' +
|
|
94
100
|
'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">' +
|
|
95
|
-
'<div style="font-weight:590;font-size:0.9375rem;color:var(--ng-text-primary)">' +
|
|
101
|
+
'<div style="font-weight:590;font-size:0.9375rem;color:var(--ng-text-primary)">' +
|
|
102
|
+
'<i class="bi ' + typeIcon + '" style="color:' + typeColor + ';margin-right:4px"></i>' +
|
|
103
|
+
escapeHtml(entity.name) +
|
|
104
|
+
'</div>' +
|
|
96
105
|
'<span class="ng-badge ng-badge-accent">' + escapeHtml(entity.type) + '</span>' +
|
|
97
106
|
'</div>' +
|
|
98
107
|
'<div style="font-size:0.8125rem;color:var(--ng-text-secondary);margin-bottom:8px;' +
|
|
@@ -107,6 +116,18 @@
|
|
|
107
116
|
'</div>';
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
function getTypeIcon(type) {
|
|
120
|
+
var icons = {
|
|
121
|
+
person: 'bi-person',
|
|
122
|
+
concept: 'bi-lightbulb',
|
|
123
|
+
organization: 'bi-building',
|
|
124
|
+
place: 'bi-geo-alt',
|
|
125
|
+
event: 'bi-calendar-event',
|
|
126
|
+
skill: 'bi-lightning-charge'
|
|
127
|
+
};
|
|
128
|
+
return icons[type] || 'bi-circle';
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
// Show entity detail panel
|
|
111
132
|
window.showEntityDetail = function(entityName) {
|
|
112
133
|
var panel = document.getElementById('entity-detail-panel');
|
|
@@ -253,7 +274,10 @@
|
|
|
253
274
|
}
|
|
254
275
|
|
|
255
276
|
function getTypeColor(type) {
|
|
256
|
-
var colors = {
|
|
277
|
+
var colors = {
|
|
278
|
+
person: '#3b82f6', concept: '#10b981', organization: '#f59e0b',
|
|
279
|
+
location: '#ef4444', skill: '#8b5cf6', event: '#ec4899'
|
|
280
|
+
};
|
|
257
281
|
return colors[type] || '#7C6AEF';
|
|
258
282
|
}
|
|
259
283
|
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
{ id: 'health-pane', icon: 'bi-heart-pulse', text: 'Health Monitor', badge: 'NEW' },
|
|
45
45
|
{ id: 'ingestion-pane', icon: 'bi-cloud-download', text: 'Ingestion', badge: 'NEW' },
|
|
46
46
|
{ id: 'entities-pane', icon: 'bi-person-badge', text: 'Entity Explorer', badge: 'NEW' },
|
|
47
|
+
{ id: 'skills-pane', icon: 'bi-lightning-charge', text: 'Skill Evolution', badge: 'NEW' },
|
|
47
48
|
{ id: 'mesh-pane', icon: 'bi-share', text: 'Mesh Peers', badge: 'NEW' }
|
|
48
49
|
]
|
|
49
50
|
},
|
|
@@ -134,6 +135,35 @@
|
|
|
134
135
|
var addProfileBtn = document.getElementById('add-profile-btn');
|
|
135
136
|
|
|
136
137
|
footer.innerHTML =
|
|
138
|
+
// v3.4.10: Cloud Backup Account Widget
|
|
139
|
+
'<div id="ng-account-widget" style="margin-bottom:10px;padding:8px;border-radius:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);cursor:pointer;" onclick="document.querySelector(\'[data-target=settings]\')?.click()">' +
|
|
140
|
+
'<div style="display:flex;align-items:center;gap:8px;">' +
|
|
141
|
+
'<span id="ng-account-avatar" style="width:28px;height:28px;border-radius:50%;background:rgba(255,255,255,0.08);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;overflow:hidden;">' +
|
|
142
|
+
'<i class="bi bi-cloud-slash" style="font-size:13px;opacity:0.4;"></i>' +
|
|
143
|
+
'</span>' +
|
|
144
|
+
'<div style="flex:1;min-width:0;">' +
|
|
145
|
+
'<div id="ng-account-name" style="font-size:12px;color:#e0e0e0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">Not connected</div>' +
|
|
146
|
+
'<div id="ng-account-status" style="font-size:10px;color:#666;">No cloud backup</div>' +
|
|
147
|
+
'</div>' +
|
|
148
|
+
'<span id="ng-account-dot" style="width:7px;height:7px;border-radius:50%;background:#444;flex-shrink:0;" title="No cloud backup"></span>' +
|
|
149
|
+
'</div>' +
|
|
150
|
+
'<div id="ng-account-actions" style="display:none;margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);">' +
|
|
151
|
+
'<div style="display:flex;gap:4px;">' +
|
|
152
|
+
'<button class="ng-btn" onclick="event.stopPropagation();connectGoogleDrive()" title="Connect Google Drive" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
153
|
+
'<i class="bi bi-google" style="color:#4285f4;"></i>' +
|
|
154
|
+
'</button>' +
|
|
155
|
+
'<button class="ng-btn" onclick="event.stopPropagation();connectGitHub()" title="Connect GitHub" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
156
|
+
'<i class="bi bi-github"></i>' +
|
|
157
|
+
'</button>' +
|
|
158
|
+
'<button class="ng-btn" onclick="event.stopPropagation();syncCloudNow()" title="Sync Now" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
159
|
+
'<i class="bi bi-cloud-upload" style="color:#00D4AA;"></i>' +
|
|
160
|
+
'</button>' +
|
|
161
|
+
'<button class="ng-btn" onclick="event.stopPropagation();exportBackup()" title="Export Backup" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
162
|
+
'<i class="bi bi-download" style="color:#f39c12;"></i>' +
|
|
163
|
+
'</button>' +
|
|
164
|
+
'</div>' +
|
|
165
|
+
'</div>' +
|
|
166
|
+
'</div>' +
|
|
137
167
|
'<div style="margin-bottom:8px">' +
|
|
138
168
|
'<div class="ng-sidebar-section-label" style="padding:0 0 4px">Profile</div>' +
|
|
139
169
|
'<div style="display:flex;gap:4px;align-items:center" id="ng-profile-container"></div>' +
|
|
@@ -359,6 +389,9 @@
|
|
|
359
389
|
case 'entities-pane':
|
|
360
390
|
if (typeof loadEntityExplorer === 'function') loadEntityExplorer();
|
|
361
391
|
break;
|
|
392
|
+
case 'skills-pane':
|
|
393
|
+
if (typeof loadSkillEvolution === 'function') loadSkillEvolution();
|
|
394
|
+
break;
|
|
362
395
|
case 'mesh-pane':
|
|
363
396
|
if (typeof loadMeshPeers === 'function') loadMeshPeers();
|
|
364
397
|
break;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
// Neural Glass — Skill Evolution Tab
|
|
2
|
+
// Browse skill performance, evolution history, and health status (v3.4.10)
|
|
3
|
+
// API: /api/behavioral/assertions (category=skill_performance, skill_correlation)
|
|
4
|
+
// /api/behavioral/tool-events (tool_name=Skill)
|
|
5
|
+
// /api/entity/list (type filter for skill entities)
|
|
6
|
+
|
|
7
|
+
(function() {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
window.loadSkillEvolution = function() {
|
|
11
|
+
fetchSkillOverview();
|
|
12
|
+
fetchSkillPerformance();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function fetchSkillOverview() {
|
|
16
|
+
var el = document.getElementById('skills-overview-cards');
|
|
17
|
+
if (!el) return;
|
|
18
|
+
|
|
19
|
+
// Compatibility notice + ECC credit + docs links
|
|
20
|
+
var noticeHtml =
|
|
21
|
+
'<div class="card" style="padding:12px 16px;margin-bottom:16px;border-left:3px solid #8b5cf6">' +
|
|
22
|
+
'<div style="font-size:0.8125rem;color:#555">' +
|
|
23
|
+
'<i class="bi bi-info-circle" style="color:#8b5cf6;margin-right:6px"></i>' +
|
|
24
|
+
'<strong>Skill Evolution</strong> currently tracks <strong>Claude Code</strong> skills. ' +
|
|
25
|
+
'The <code>/api/v3/tool-event</code> endpoint accepts events from any IDE client. ' +
|
|
26
|
+
'Enhanced observation support available with ' +
|
|
27
|
+
'<a href="https://github.com/affaan-m/everything-claude-code" target="_blank" style="color:#8b5cf6">Everything Claude Code (ECC)</a> ' +
|
|
28
|
+
'via <code>slm ingest --source ecc</code>.' +
|
|
29
|
+
'</div>' +
|
|
30
|
+
'<div style="font-size:0.75rem;color:#888;margin-top:8px">' +
|
|
31
|
+
'<a href="https://superlocalmemory.com/skill-evolution" target="_blank" style="color:#8b5cf6;margin-right:12px"><i class="bi bi-globe"></i> Learn more</a>' +
|
|
32
|
+
'<a href="https://github.com/qualixar/superlocalmemory/blob/main/docs/skill-evolution.md" target="_blank" style="color:#8b5cf6"><i class="bi bi-book"></i> Documentation</a>' +
|
|
33
|
+
'</div>' +
|
|
34
|
+
'</div>';
|
|
35
|
+
el.innerHTML = noticeHtml + '<div id="skills-overview-inner"></div>';
|
|
36
|
+
el = document.getElementById('skills-overview-inner');
|
|
37
|
+
|
|
38
|
+
// Fetch tool events for Skill calls + assertions for skill_performance
|
|
39
|
+
Promise.all([
|
|
40
|
+
fetch('/api/behavioral/tool-events?tool_name=Skill&limit=500').then(function(r) { return r.json(); }),
|
|
41
|
+
fetch('/api/behavioral/assertions?category=skill_performance&limit=50').then(function(r) { return r.json(); }),
|
|
42
|
+
fetch('/api/behavioral/assertions?category=skill_correlation&limit=20').then(function(r) { return r.json(); }),
|
|
43
|
+
]).then(function(results) {
|
|
44
|
+
var events = results[0].events || [];
|
|
45
|
+
var perfAssertions = results[1].assertions || [];
|
|
46
|
+
var corrAssertions = results[2].assertions || [];
|
|
47
|
+
|
|
48
|
+
// Count unique skills from events
|
|
49
|
+
var skillNames = {};
|
|
50
|
+
events.forEach(function(e) {
|
|
51
|
+
var name = extractSkillName(e);
|
|
52
|
+
if (name) skillNames[name] = (skillNames[name] || 0) + 1;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
var html = '<div class="row g-3 mb-4">' +
|
|
56
|
+
overviewCard('Total Skill Events', events.length, 'bi-lightning-charge', 'var(--ng-accent)') +
|
|
57
|
+
overviewCard('Unique Skills', Object.keys(skillNames).length, 'bi-grid-3x3', '#8b5cf6') +
|
|
58
|
+
overviewCard('Performance Assertions', perfAssertions.length, 'bi-graph-up', '#10b981') +
|
|
59
|
+
overviewCard('Skill Correlations', corrAssertions.length, 'bi-link-45deg', '#f59e0b') +
|
|
60
|
+
'</div>';
|
|
61
|
+
|
|
62
|
+
el.innerHTML = html;
|
|
63
|
+
}).catch(function() {
|
|
64
|
+
el.innerHTML = '<div class="alert alert-warning">Could not load skill overview</div>';
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function fetchSkillPerformance() {
|
|
69
|
+
var el = document.getElementById('skills-list');
|
|
70
|
+
if (!el) return;
|
|
71
|
+
|
|
72
|
+
Promise.all([
|
|
73
|
+
fetch('/api/behavioral/assertions?category=skill_performance&limit=50').then(function(r) { return r.json(); }),
|
|
74
|
+
fetch('/api/behavioral/assertions?category=skill_correlation&limit=20').then(function(r) { return r.json(); }),
|
|
75
|
+
fetch('/api/behavioral/tool-events?tool_name=Skill&limit=500').then(function(r) { return r.json(); }),
|
|
76
|
+
]).then(function(results) {
|
|
77
|
+
var perfAssertions = results[0].assertions || [];
|
|
78
|
+
var corrAssertions = results[1].assertions || [];
|
|
79
|
+
var events = results[2].events || [];
|
|
80
|
+
|
|
81
|
+
var html = '';
|
|
82
|
+
|
|
83
|
+
// Section 1: Skill Performance
|
|
84
|
+
html += '<h5 style="margin-bottom:16px"><i class="bi bi-lightning-charge" style="color:#8b5cf6"></i> Skill Performance</h5>';
|
|
85
|
+
|
|
86
|
+
if (perfAssertions.length === 0 && events.length === 0) {
|
|
87
|
+
html += '<div class="card" style="padding:24px;text-align:center;color:#888">' +
|
|
88
|
+
'<i class="bi bi-lightning-charge" style="font-size:2.5rem;display:block;margin-bottom:12px;opacity:0.3"></i>' +
|
|
89
|
+
'<div style="font-size:1rem;margin-bottom:4px;color:#444">No skill performance data yet</div>' +
|
|
90
|
+
'<div style="font-size:0.8125rem">' +
|
|
91
|
+
'Skill tracking starts automatically after the enriched hook captures data.<br>' +
|
|
92
|
+
'Use skills in your sessions — performance assertions will appear after consolidation.' +
|
|
93
|
+
'</div>' +
|
|
94
|
+
'</div>';
|
|
95
|
+
} else if (perfAssertions.length > 0) {
|
|
96
|
+
html += '<div class="row g-3">';
|
|
97
|
+
perfAssertions.forEach(function(a) {
|
|
98
|
+
html += renderSkillCard(a);
|
|
99
|
+
});
|
|
100
|
+
html += '</div>';
|
|
101
|
+
} else {
|
|
102
|
+
// We have events but no assertions yet (need consolidation)
|
|
103
|
+
html += '<div class="card" style="padding:16px;margin-bottom:16px">' +
|
|
104
|
+
'<div style="font-size:0.875rem;color:#555">' +
|
|
105
|
+
'<i class="bi bi-info-circle" style="color:#8b5cf6;margin-right:6px"></i>' +
|
|
106
|
+
events.length + ' skill events collected. Run consolidation to generate performance assertions.' +
|
|
107
|
+
'</div>' +
|
|
108
|
+
'</div>';
|
|
109
|
+
|
|
110
|
+
// Show raw event summary
|
|
111
|
+
var skillCounts = {};
|
|
112
|
+
events.forEach(function(e) {
|
|
113
|
+
var name = extractSkillName(e);
|
|
114
|
+
if (name) skillCounts[name] = (skillCounts[name] || 0) + 1;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
html += '<div class="row g-3">';
|
|
118
|
+
Object.keys(skillCounts).sort(function(a, b) {
|
|
119
|
+
return skillCounts[b] - skillCounts[a];
|
|
120
|
+
}).forEach(function(name) {
|
|
121
|
+
html += '<div class="col-md-6 col-lg-4"><div class="card" style="padding:16px;border-left:3px solid #8b5cf6">' +
|
|
122
|
+
'<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
123
|
+
'<div style="font-weight:600;font-size:0.9375rem">' +
|
|
124
|
+
'<i class="bi bi-lightning-charge" style="color:#8b5cf6;margin-right:4px"></i>' +
|
|
125
|
+
escapeHtml(name) +
|
|
126
|
+
'</div>' +
|
|
127
|
+
'<span class="badge" style="background:#8b5cf620;color:#8b5cf6;font-size:0.75rem">' + skillCounts[name] + ' events</span>' +
|
|
128
|
+
'</div>' +
|
|
129
|
+
'</div></div>';
|
|
130
|
+
});
|
|
131
|
+
html += '</div>';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Section 2: Skill Correlations
|
|
135
|
+
if (corrAssertions.length > 0) {
|
|
136
|
+
html += '<h5 style="margin-top:32px;margin-bottom:16px"><i class="bi bi-link-45deg" style="color:#f59e0b"></i> Skill Correlations</h5>';
|
|
137
|
+
html += '<div class="row g-3">';
|
|
138
|
+
corrAssertions.forEach(function(a) {
|
|
139
|
+
html += '<div class="col-md-6"><div class="card" style="padding:12px">' +
|
|
140
|
+
'<div style="font-size:0.875rem">' +
|
|
141
|
+
'<strong>' + escapeHtml(a.trigger_condition || '') + '</strong>' +
|
|
142
|
+
'</div>' +
|
|
143
|
+
'<div style="font-size:0.8125rem;color:#555;margin-top:4px">' +
|
|
144
|
+
escapeHtml(a.action || '') +
|
|
145
|
+
'</div>' +
|
|
146
|
+
'<div style="font-size:0.75rem;color:#888;margin-top:4px">' +
|
|
147
|
+
'Confidence: ' + ((a.confidence || 0) * 100).toFixed(0) + '%' +
|
|
148
|
+
'</div>' +
|
|
149
|
+
'</div></div>';
|
|
150
|
+
});
|
|
151
|
+
html += '</div>';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
el.innerHTML = html;
|
|
155
|
+
}).catch(function(err) {
|
|
156
|
+
el.innerHTML = '<div class="text-center" style="padding:24px;color:var(--ng-text-tertiary)">' +
|
|
157
|
+
'Error loading skill data: ' + err.message + '</div>';
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function renderSkillCard(assertion) {
|
|
162
|
+
var conf = assertion.confidence || 0;
|
|
163
|
+
var confPct = (conf * 100).toFixed(0);
|
|
164
|
+
var confColor = conf >= 0.7 ? '#10b981' : conf >= 0.5 ? '#f59e0b' : '#ef4444';
|
|
165
|
+
|
|
166
|
+
// Extract skill name from trigger_condition
|
|
167
|
+
var skillName = (assertion.trigger_condition || '').replace('when considering skill ', '');
|
|
168
|
+
|
|
169
|
+
return '<div class="col-md-6 col-lg-4">' +
|
|
170
|
+
'<div class="card" style="padding:16px;border-left:3px solid #8b5cf6;cursor:pointer">' +
|
|
171
|
+
'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">' +
|
|
172
|
+
'<div style="font-weight:600;font-size:0.9375rem">' +
|
|
173
|
+
'<i class="bi bi-lightning-charge" style="color:#8b5cf6;margin-right:4px"></i>' +
|
|
174
|
+
escapeHtml(skillName) +
|
|
175
|
+
'</div>' +
|
|
176
|
+
'<span class="badge" style="background:' + confColor + ';color:#fff;font-size:0.75rem">' +
|
|
177
|
+
confPct + '%' +
|
|
178
|
+
'</span>' +
|
|
179
|
+
'</div>' +
|
|
180
|
+
'<div style="font-size:0.8125rem;color:#555;margin-bottom:8px">' +
|
|
181
|
+
escapeHtml(assertion.action || 'No performance data yet') +
|
|
182
|
+
'</div>' +
|
|
183
|
+
'<div style="display:flex;justify-content:space-between;align-items:center;font-size:0.75rem;color:#888">' +
|
|
184
|
+
'<span>Evidence: ' + (assertion.evidence_count || 0) + ' invocations</span>' +
|
|
185
|
+
'<span>Reinforced: ' + (assertion.reinforcement_count || 0) + 'x</span>' +
|
|
186
|
+
'</div>' +
|
|
187
|
+
'</div>' +
|
|
188
|
+
'</div>';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function extractSkillName(event) {
|
|
192
|
+
var input = event.input_summary || '';
|
|
193
|
+
var output = event.output_summary || '';
|
|
194
|
+
|
|
195
|
+
// Try input_summary (enriched hook format)
|
|
196
|
+
if (input) {
|
|
197
|
+
try {
|
|
198
|
+
var inp = JSON.parse(input);
|
|
199
|
+
if (inp.skill) return inp.skill;
|
|
200
|
+
} catch(e) {}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Try output_summary (ECC ingestion format)
|
|
204
|
+
if (output) {
|
|
205
|
+
try {
|
|
206
|
+
var out = JSON.parse(output);
|
|
207
|
+
if (out.commandName) return out.commandName;
|
|
208
|
+
} catch(e) {}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function overviewCard(label, value, icon, color) {
|
|
215
|
+
return '<div class="col-md-3 col-6"><div class="card" style="padding:12px;text-align:center">' +
|
|
216
|
+
'<i class="bi ' + icon + '" style="color:' + color + ';font-size:1.125rem;display:block;margin-bottom:4px"></i>' +
|
|
217
|
+
'<div style="font-size:1.25rem;font-weight:600">' + value + '</div>' +
|
|
218
|
+
'<div style="font-size:0.75rem;text-transform:uppercase;letter-spacing:0.06em;color:#888">' + label + '</div>' +
|
|
219
|
+
'</div></div>';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function escapeHtml(s) {
|
|
223
|
+
var d = document.createElement('div');
|
|
224
|
+
d.textContent = s || '';
|
|
225
|
+
return d.innerHTML;
|
|
226
|
+
}
|
|
227
|
+
})();
|