recallnest 1.4.1 → 2.2.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/.dockerignore +11 -0
- package/CLAUDE.md +7 -3
- package/Dockerfile +20 -0
- package/README.md +202 -130
- package/README_CN.md +263 -190
- package/ROADMAP.md +128 -81
- package/SKILL.md +250 -0
- package/assets/dashboard.png +0 -0
- package/assets/knowledge-graph.png +0 -0
- package/assets/screenshots/ui-full.png +0 -0
- package/assets/screenshots/ui-overview.png +0 -0
- package/assets/screenshots/ui-search.png +0 -0
- package/assets/ui/app.js +227 -3
- package/assets/ui/index.html +81 -11
- package/assets/ui/styles.css +178 -2
- package/bin/recallnest.mjs +62 -0
- package/bun.lock +337 -0
- package/daily-recall.sh +23 -0
- package/daily-reflections/2026-04-02.md +35 -0
- package/daily-reflections/2026-04-03.md +37 -0
- package/daily-reflections/2026-04-04.md +37 -0
- package/daily-reflections/2026-04-05.md +35 -0
- package/daily-reflections/2026-04-06.md +36 -0
- package/daily-reflections/2026-04-07.md +35 -0
- package/daily-reflections/2026-04-08.md +35 -0
- package/daily-reflections/2026-04-12.md +35 -0
- package/data/alias-map.example.json +22 -0
- package/docs/plans/2026-04-01-recall-boost.md +100 -0
- package/docs/superpowers/plans/2026-04-11-constructive-retrieval-emotion.md +1195 -0
- package/docs/superpowers/specs/2026-04-11-constructive-retrieval-emotion-design.md +398 -0
- package/glama.json +6 -0
- package/integrations/setup-all.sh +31 -0
- package/package.json +10 -3
- package/plan-philosophy-upgrades.md +363 -0
- package/plan-product-upgrades.md +155 -0
- package/plan.md +140 -0
- package/research-memory-evolution.md +440 -0
- package/research-rl-agent-memory.md +330 -0
- package/scripts/backfill-anchors.ts +130 -0
- package/scripts/diagnose-vector-search.ts +132 -0
- package/scripts/migrate-language.ts +75 -0
- package/scripts/verify-emotion-decay.ts +148 -0
- package/src/__tests__/activity-counter.test.ts +96 -0
- package/src/__tests__/admission-control.test.ts +206 -0
- package/src/__tests__/anchor-generator.test.ts +67 -0
- package/src/__tests__/babel-integration.test.ts +139 -0
- package/src/__tests__/capture-engine.test.ts +5 -3
- package/src/__tests__/cascade-forget.test.ts +1 -1
- package/src/__tests__/cc8-cc1-essential-context.test.ts +298 -0
- package/src/__tests__/circuit-breaker.test.ts +99 -0
- package/src/__tests__/cluster-consolidation.test.ts +712 -0
- package/src/__tests__/confidence-metadata.test.ts +125 -0
- package/src/__tests__/confidence-tracker.test.ts +53 -0
- package/src/__tests__/consolidation-engine.test.ts +1 -1
- package/src/__tests__/constructive-retrieval.test.ts +509 -0
- package/src/__tests__/context-collapse-renderer.test.ts +214 -0
- package/src/__tests__/context-reconstructor.test.ts +65 -0
- package/src/__tests__/conversation-importer.test.ts +692 -0
- package/src/__tests__/data-checkup.test.ts +225 -0
- package/src/__tests__/decay-exemption.test.ts +84 -0
- package/src/__tests__/dedup-guard.test.ts +39 -0
- package/src/__tests__/distill-lock.test.ts +176 -0
- package/src/__tests__/dream-pipeline.test.ts +232 -0
- package/src/__tests__/emotion-decay.test.ts +46 -0
- package/src/__tests__/emotion-detector.test.ts +56 -0
- package/src/__tests__/emotion-phase1-integration.test.ts +153 -0
- package/src/__tests__/emotion-retrieval.test.ts +56 -0
- package/src/__tests__/ethics-layer.test.ts +476 -0
- package/src/__tests__/feature-flags-integration.test.ts +54 -0
- package/src/__tests__/frequency-tracker.test.ts +113 -0
- package/src/__tests__/graph-export.test.ts +489 -0
- package/src/__tests__/interference-detector.test.ts +203 -0
- package/src/__tests__/mcp-efficiency.test.ts +288 -0
- package/src/__tests__/memory-evolution-b2-b3-e1.test.ts +243 -0
- package/src/__tests__/memory-evolution.test.ts +271 -0
- package/src/__tests__/memory-lint.test.ts +271 -0
- package/src/__tests__/narrative-architecture.test.ts +456 -0
- package/src/__tests__/pending-review.test.ts +77 -0
- package/src/__tests__/post-retrieval-filter.test.ts +178 -0
- package/src/__tests__/prediction-engine.test.ts +349 -0
- package/src/__tests__/predictive-prospective-memory.test.ts +416 -0
- package/src/__tests__/preference-slots.test.ts +99 -0
- package/src/__tests__/query-expander-alias.test.ts +89 -0
- package/src/__tests__/recall-governor.test.ts +227 -0
- package/src/__tests__/resume-context.test.ts +156 -1
- package/src/__tests__/salience-scoring.test.ts +106 -0
- package/src/__tests__/session-distiller.test.ts +611 -0
- package/src/__tests__/skill-engine.test.ts +546 -0
- package/src/__tests__/skill-promotion.test.ts +349 -0
- package/src/__tests__/supersede-chain.test.ts +154 -0
- package/src/__tests__/temporal-validity.test.ts +160 -0
- package/src/__tests__/topic-tag.test.ts +113 -0
- package/src/__tests__/trusted-memory.test.ts +828 -0
- package/src/__tests__/write-verifier.test.ts +122 -0
- package/src/access-tracker.ts +3 -2
- package/src/activity-counter.ts +109 -0
- package/src/adaptive-retrieval.ts +6 -5
- package/src/admission-control.ts +163 -0
- package/src/anchor-generator.ts +80 -0
- package/src/api-server.ts +10 -0
- package/src/audit-log.ts +122 -0
- package/src/auto-gc.ts +98 -29
- package/src/auto-recall.ts +33 -4
- package/src/capture-engine.ts +372 -23
- package/src/cascade-forget.ts +3 -2
- package/src/cli.ts +107 -5
- package/src/compat.ts +22 -0
- package/src/confidence-tracker.ts +210 -47
- package/src/conflict-store.ts +12 -3
- package/src/consolidation-engine.ts +296 -8
- package/src/context-collapse-renderer.ts +222 -0
- package/src/context-composer-stable-selection.ts +5 -1
- package/src/context-composer.ts +239 -4
- package/src/context-reconstructor.ts +404 -0
- package/src/conversation-importer.ts +441 -0
- package/src/data-checkup.ts +232 -0
- package/src/decay-engine.ts +84 -0
- package/src/distill-lock.ts +122 -0
- package/src/doctor.ts +8 -10
- package/src/dream-pipeline.ts +227 -0
- package/src/emotion-detector.ts +90 -0
- package/src/eval.ts +3 -2
- package/src/forget-engine.ts +269 -0
- package/src/frequency-tracker.ts +175 -0
- package/src/graph-export.ts +644 -0
- package/src/ingest.ts +36 -6
- package/src/interference-detector.ts +187 -0
- package/src/kg-extractor.ts +15 -24
- package/src/kg-store.ts +2 -1
- package/src/language-hook.ts +67 -0
- package/src/llm-client.ts +289 -6
- package/src/mcp-server.ts +707 -80
- package/src/memory-assets.ts +22 -5
- package/src/memory-evolution.ts +365 -0
- package/src/memory-lint.ts +429 -0
- package/src/memory-output.ts +69 -0
- package/src/memory-schema.ts +74 -2
- package/src/narrative-schema.ts +79 -0
- package/src/narrative-tagger.ts +164 -0
- package/src/pii-detector.ts +98 -0
- package/src/post-retrieval-filter.ts +76 -0
- package/src/prediction-engine.ts +253 -0
- package/src/preference-slots.ts +65 -2
- package/src/prospective-memory.ts +215 -0
- package/src/query-expander.ts +67 -0
- package/src/recall-governor.ts +183 -0
- package/src/retention-policy.ts +90 -0
- package/src/retriever.ts +828 -55
- package/src/rif.ts +34 -8
- package/src/runtime-config.ts +23 -8
- package/src/scope-inventory.ts +4 -3
- package/src/scope-policy.ts +4 -0
- package/src/session-distiller.ts +307 -0
- package/src/session-output.ts +40 -0
- package/src/session-schema.ts +37 -0
- package/src/session-store.ts +7 -2
- package/src/skill-engine.ts +208 -0
- package/src/skill-promotion.ts +293 -0
- package/src/skill-schema.ts +39 -0
- package/src/store.ts +96 -10
- package/src/topic-tag.ts +66 -0
- package/src/tracker.ts +3 -1
- package/src/ui-server.ts +80 -8
- package/src/workflow-observation-scope-review.ts +2 -1
- package/src/workflow-observation-store.ts +2 -1
- package/src/write-verifier.ts +126 -0
package/assets/ui/app.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const queryInput = document.getElementById('queryInput');
|
|
2
2
|
const profileInput = document.getElementById('profileInput');
|
|
3
3
|
const scopeInput = document.getElementById('scopeInput');
|
|
4
|
+
const topicTagInput = document.getElementById('topicTagInput');
|
|
4
5
|
const limitInput = document.getElementById('limitInput');
|
|
5
6
|
const formatInput = document.getElementById('formatInput');
|
|
6
7
|
const resultOutput = document.getElementById('resultOutput');
|
|
@@ -24,7 +25,7 @@ const toggleTraceButton = document.getElementById('toggleTraceButton');
|
|
|
24
25
|
const viewTabs = Array.from(document.querySelectorAll('.view-tab'));
|
|
25
26
|
const quickCards = Array.from(document.querySelectorAll('[data-quick-action]'));
|
|
26
27
|
|
|
27
|
-
let currentView = '
|
|
28
|
+
let currentView = 'dashboard';
|
|
28
29
|
let lastItems = [];
|
|
29
30
|
let lastMode = 'search';
|
|
30
31
|
let lastArtifact = null;
|
|
@@ -38,6 +39,7 @@ let lastExports = [];
|
|
|
38
39
|
let currentViewFilter = '';
|
|
39
40
|
let activeAssetTag = '';
|
|
40
41
|
let lastDirtyBriefCount = 0;
|
|
42
|
+
let lastSkills = [];
|
|
41
43
|
|
|
42
44
|
async function api(path, payload) {
|
|
43
45
|
const response = await fetch(path, {
|
|
@@ -57,6 +59,7 @@ function currentPayload() {
|
|
|
57
59
|
query: queryInput.value.trim(),
|
|
58
60
|
profile: profileInput.value,
|
|
59
61
|
scope: scopeInput.value.trim() || undefined,
|
|
62
|
+
topicTag: topicTagInput.value || undefined,
|
|
60
63
|
limit: Number(limitInput.value) || 5,
|
|
61
64
|
};
|
|
62
65
|
}
|
|
@@ -526,6 +529,13 @@ function renderMainSurface() {
|
|
|
526
529
|
return;
|
|
527
530
|
}
|
|
528
531
|
|
|
532
|
+
if (currentView === 'skills') {
|
|
533
|
+
resultTitle.textContent = 'Executable Skills';
|
|
534
|
+
resultMeta.textContent = `Skills: ${lastSkills.length}`;
|
|
535
|
+
renderSkillsView(lastSkills);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
|
|
529
539
|
if (currentView === 'exports') {
|
|
530
540
|
const filtered = filterExports(lastExports);
|
|
531
541
|
resultTitle.textContent = 'Export Artifacts';
|
|
@@ -606,6 +616,97 @@ async function loadExports() {
|
|
|
606
616
|
}
|
|
607
617
|
}
|
|
608
618
|
|
|
619
|
+
async function loadSkills() {
|
|
620
|
+
try {
|
|
621
|
+
const data = await api('/api/skills');
|
|
622
|
+
lastSkills = data.items || [];
|
|
623
|
+
if (currentView === 'skills') renderMainSurface();
|
|
624
|
+
} catch (error) {
|
|
625
|
+
statusLine.textContent = String(error.message || error);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function renderSkillsView(items) {
|
|
630
|
+
if (!items || items.length === 0) {
|
|
631
|
+
resultCards.textContent = '';
|
|
632
|
+
const emptyDiv = document.createElement('div');
|
|
633
|
+
emptyDiv.className = 'empty-state';
|
|
634
|
+
emptyDiv.textContent = 'No skills stored yet. Use store_skill via MCP to add executable skills.';
|
|
635
|
+
resultCards.appendChild(emptyDiv);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
resultCards.textContent = '';
|
|
640
|
+
for (const item of items) {
|
|
641
|
+
const article = document.createElement('article');
|
|
642
|
+
article.className = 'list-card result-card';
|
|
643
|
+
article.dataset.cardId = item.shortId;
|
|
644
|
+
|
|
645
|
+
const head = document.createElement('div');
|
|
646
|
+
head.className = 'list-card-head';
|
|
647
|
+
const nameEl = document.createElement('strong');
|
|
648
|
+
nameEl.textContent = item.name;
|
|
649
|
+
const typeChip = document.createElement('span');
|
|
650
|
+
typeChip.className = 'card-chip';
|
|
651
|
+
typeChip.textContent = item.type;
|
|
652
|
+
head.appendChild(nameEl);
|
|
653
|
+
head.appendChild(typeChip);
|
|
654
|
+
|
|
655
|
+
const desc = document.createElement('p');
|
|
656
|
+
desc.className = 'result-snippet';
|
|
657
|
+
desc.textContent = item.description;
|
|
658
|
+
|
|
659
|
+
const meta = document.createElement('div');
|
|
660
|
+
meta.className = 'result-card-meta';
|
|
661
|
+
const triggerSpan = document.createElement('span');
|
|
662
|
+
triggerSpan.textContent = 'Trigger: ' + item.trigger;
|
|
663
|
+
const scopeSpan = document.createElement('span');
|
|
664
|
+
scopeSpan.textContent = item.scope;
|
|
665
|
+
meta.appendChild(triggerSpan);
|
|
666
|
+
meta.appendChild(scopeSpan);
|
|
667
|
+
|
|
668
|
+
const actions = document.createElement('div');
|
|
669
|
+
actions.className = 'result-card-actions';
|
|
670
|
+
const detailBtn = document.createElement('button');
|
|
671
|
+
detailBtn.className = 'card-chip';
|
|
672
|
+
detailBtn.dataset.toggleId = item.shortId;
|
|
673
|
+
detailBtn.textContent = 'Details';
|
|
674
|
+
const copyBtn = document.createElement('button');
|
|
675
|
+
copyBtn.className = 'card-chip';
|
|
676
|
+
copyBtn.dataset.copyText = encodeURIComponent(item.implementation);
|
|
677
|
+
copyBtn.textContent = 'Copy Code';
|
|
678
|
+
actions.appendChild(detailBtn);
|
|
679
|
+
actions.appendChild(copyBtn);
|
|
680
|
+
|
|
681
|
+
const detail = document.createElement('div');
|
|
682
|
+
detail.className = 'result-card-detail';
|
|
683
|
+
const implBlock = document.createElement('div');
|
|
684
|
+
implBlock.className = 'detail-block';
|
|
685
|
+
const implLabel = document.createElement('strong');
|
|
686
|
+
implLabel.textContent = 'Implementation';
|
|
687
|
+
const implPre = document.createElement('pre');
|
|
688
|
+
implPre.textContent = item.implementation;
|
|
689
|
+
implBlock.appendChild(implLabel);
|
|
690
|
+
implBlock.appendChild(implPre);
|
|
691
|
+
const tagsBlock = document.createElement('div');
|
|
692
|
+
tagsBlock.className = 'detail-block';
|
|
693
|
+
const tagsLabel = document.createElement('strong');
|
|
694
|
+
tagsLabel.textContent = 'Tags: ' + ((item.tags || []).join(', ') || '-');
|
|
695
|
+
tagsBlock.appendChild(tagsLabel);
|
|
696
|
+
detail.appendChild(implBlock);
|
|
697
|
+
detail.appendChild(tagsBlock);
|
|
698
|
+
|
|
699
|
+
article.appendChild(head);
|
|
700
|
+
article.appendChild(desc);
|
|
701
|
+
article.appendChild(meta);
|
|
702
|
+
article.appendChild(actions);
|
|
703
|
+
article.appendChild(detail);
|
|
704
|
+
resultCards.appendChild(article);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
bindResultCardActions();
|
|
708
|
+
}
|
|
709
|
+
|
|
609
710
|
async function loadStats() {
|
|
610
711
|
try {
|
|
611
712
|
const data = await api('/api/stats');
|
|
@@ -723,6 +824,12 @@ async function handleQuickAction(action) {
|
|
|
723
824
|
await runMode('distill');
|
|
724
825
|
return;
|
|
725
826
|
}
|
|
827
|
+
if (action === 'skills') {
|
|
828
|
+
setActiveView('skills');
|
|
829
|
+
await loadSkills();
|
|
830
|
+
renderMainSurface();
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
726
833
|
if (action === 'pin') {
|
|
727
834
|
pinMemoryId.focus();
|
|
728
835
|
pinMemoryId.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
@@ -737,6 +844,9 @@ viewTabs.forEach((tab) => {
|
|
|
737
844
|
if (view === 'pins') {
|
|
738
845
|
await loadPins();
|
|
739
846
|
}
|
|
847
|
+
if (view === 'skills') {
|
|
848
|
+
await loadSkills();
|
|
849
|
+
}
|
|
740
850
|
if (view === 'exports') {
|
|
741
851
|
await loadExports();
|
|
742
852
|
}
|
|
@@ -794,7 +904,7 @@ assetTagBar.addEventListener('click', (event) => {
|
|
|
794
904
|
|
|
795
905
|
queryInput.value = 'telegram bridge';
|
|
796
906
|
profileInput.value = 'debug';
|
|
797
|
-
setActiveView('
|
|
907
|
+
setActiveView('dashboard');
|
|
798
908
|
renderArtifactBar();
|
|
799
909
|
renderStats();
|
|
800
910
|
renderPinsPanel();
|
|
@@ -804,4 +914,118 @@ renderMainSurface();
|
|
|
804
914
|
loadPins();
|
|
805
915
|
loadExports();
|
|
806
916
|
loadStats();
|
|
807
|
-
|
|
917
|
+
|
|
918
|
+
// ---------------------------------------------------------------------------
|
|
919
|
+
// Dashboard
|
|
920
|
+
// ---------------------------------------------------------------------------
|
|
921
|
+
|
|
922
|
+
const dashTotal = document.getElementById('dashTotal');
|
|
923
|
+
const dashHealth = document.getElementById('dashHealth');
|
|
924
|
+
const dashWeek = document.getElementById('dashWeek');
|
|
925
|
+
const dashMonth = document.getElementById('dashMonth');
|
|
926
|
+
const dashCategoryBars = document.getElementById('dashCategoryBars');
|
|
927
|
+
const dashLintSummary = document.getElementById('dashLintSummary');
|
|
928
|
+
const dashStaleList = document.getElementById('dashStaleList');
|
|
929
|
+
const dashStaleCount = document.getElementById('dashStaleCount');
|
|
930
|
+
const dashboardView = document.getElementById('dashboardView');
|
|
931
|
+
const searchView = document.getElementById('searchView');
|
|
932
|
+
const resultView = document.getElementById('resultView');
|
|
933
|
+
const sideView = document.getElementById('sideView');
|
|
934
|
+
const refreshDashboard = document.getElementById('refreshDashboard');
|
|
935
|
+
|
|
936
|
+
const CATEGORY_COLORS = {
|
|
937
|
+
profile: '#f59e0b',
|
|
938
|
+
preferences: '#3b82f6',
|
|
939
|
+
entities: '#10b981',
|
|
940
|
+
events: '#6b7280',
|
|
941
|
+
cases: '#ef4444',
|
|
942
|
+
patterns: '#8b5cf6',
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
async function loadDashboard() {
|
|
946
|
+
try {
|
|
947
|
+
const [statsRes, lintRes, staleRes] = await Promise.all([
|
|
948
|
+
fetch('/api/dashboard-stats'),
|
|
949
|
+
fetch('/api/lint-summary'),
|
|
950
|
+
fetch('/api/stale-memories'),
|
|
951
|
+
]);
|
|
952
|
+
const stats = await statsRes.json();
|
|
953
|
+
const lint = await lintRes.json();
|
|
954
|
+
const stale = await staleRes.json();
|
|
955
|
+
|
|
956
|
+
dashTotal.textContent = stats.totalCount.toLocaleString();
|
|
957
|
+
dashWeek.textContent = '+' + stats.growth.thisWeek;
|
|
958
|
+
dashMonth.textContent = '+' + stats.growth.thisMonth;
|
|
959
|
+
dashHealth.textContent = lint.healthScore + '/100';
|
|
960
|
+
dashHealth.className = 'stat-value ' + (lint.healthScore >= 80 ? 'good' : lint.healthScore >= 50 ? 'warn' : 'bad');
|
|
961
|
+
|
|
962
|
+
const cats = stats.categoryCounts || {};
|
|
963
|
+
const maxCount = Math.max(1, ...Object.values(cats));
|
|
964
|
+
const barHtml = Object.entries(cats)
|
|
965
|
+
.sort((a, b) => b[1] - a[1])
|
|
966
|
+
.map(([cat, count]) => {
|
|
967
|
+
const pct = Math.round((count / maxCount) * 100);
|
|
968
|
+
const color = CATEGORY_COLORS[cat] || '#6b7280';
|
|
969
|
+
return '<div class="bar-row">'
|
|
970
|
+
+ '<span class="bar-label">' + escapeHtml(cat) + '</span>'
|
|
971
|
+
+ '<div class="bar-track"><div class="bar-fill" style="width:' + pct + '%;background:' + color + '"></div></div>'
|
|
972
|
+
+ '<span class="bar-count">' + count + '</span>'
|
|
973
|
+
+ '</div>';
|
|
974
|
+
}).join('');
|
|
975
|
+
dashCategoryBars.innerHTML = barHtml;
|
|
976
|
+
|
|
977
|
+
const s = lint.summary;
|
|
978
|
+
const issues = [];
|
|
979
|
+
if (s.contradictions > 0) issues.push(s.contradictions + ' contradiction' + (s.contradictions > 1 ? 's' : ''));
|
|
980
|
+
if (s.duplicates > 0) issues.push(s.duplicates + ' duplicate' + (s.duplicates > 1 ? 's' : ''));
|
|
981
|
+
if (s.staleMemories > 0) issues.push(s.staleMemories + ' stale');
|
|
982
|
+
if (s.orphans > 0) issues.push(s.orphans + ' orphan' + (s.orphans > 1 ? 's' : ''));
|
|
983
|
+
if (issues.length === 0) {
|
|
984
|
+
dashLintSummary.innerHTML = '<div class="lint-ok">All Clear — no issues found</div>';
|
|
985
|
+
} else {
|
|
986
|
+
dashLintSummary.innerHTML = issues.map(function(i) {
|
|
987
|
+
return '<div class="lint-issue">' + escapeHtml(i) + '</div>';
|
|
988
|
+
}).join('');
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
dashStaleCount.textContent = stale.count;
|
|
992
|
+
if (stale.items.length === 0) {
|
|
993
|
+
dashStaleList.innerHTML = '<div class="lint-ok">No stale memories</div>';
|
|
994
|
+
} else {
|
|
995
|
+
dashStaleList.innerHTML = stale.items
|
|
996
|
+
.map(function(item) { return '<div class="stale-item">' + escapeHtml(item.detail) + '</div>'; })
|
|
997
|
+
.join('');
|
|
998
|
+
}
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
console.error('Dashboard load error:', err);
|
|
1001
|
+
dashLintSummary.textContent = 'Failed to load dashboard data';
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const originalViewTabs = Array.from(document.querySelectorAll('.view-tab'));
|
|
1006
|
+
for (const tab of originalViewTabs) {
|
|
1007
|
+
tab.addEventListener('click', () => {
|
|
1008
|
+
const view = tab.dataset.view;
|
|
1009
|
+
originalViewTabs.forEach(t => t.classList.toggle('is-active', t === tab));
|
|
1010
|
+
|
|
1011
|
+
if (view === 'dashboard') {
|
|
1012
|
+
if (dashboardView) dashboardView.classList.remove('is-hidden');
|
|
1013
|
+
if (searchView) searchView.classList.add('is-hidden');
|
|
1014
|
+
if (resultView) resultView.classList.add('is-hidden');
|
|
1015
|
+
if (sideView) sideView.classList.add('is-hidden');
|
|
1016
|
+
} else {
|
|
1017
|
+
if (dashboardView) dashboardView.classList.add('is-hidden');
|
|
1018
|
+
if (searchView) searchView.classList.remove('is-hidden');
|
|
1019
|
+
if (resultView) resultView.classList.remove('is-hidden');
|
|
1020
|
+
if (sideView) sideView.classList.remove('is-hidden');
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
currentView = view;
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (refreshDashboard) {
|
|
1028
|
+
refreshDashboard.addEventListener('click', loadDashboard);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
loadDashboard();
|
package/assets/ui/index.html
CHANGED
|
@@ -11,25 +11,30 @@
|
|
|
11
11
|
<header class="hero">
|
|
12
12
|
<div>
|
|
13
13
|
<p class="eyebrow">RecallNest</p>
|
|
14
|
-
<h1>
|
|
15
|
-
<p class="subtitle">Search,
|
|
14
|
+
<h1>Shared memory layer workbench.</h1>
|
|
15
|
+
<p class="subtitle">Search, distill, pin, import, skills.</p>
|
|
16
16
|
</div>
|
|
17
17
|
<div class="hero-badge">
|
|
18
|
-
<span>MCP
|
|
19
|
-
<span>Hybrid
|
|
20
|
-
<span>
|
|
18
|
+
<span>38 MCP Tools</span>
|
|
19
|
+
<span>6-Channel Hybrid</span>
|
|
20
|
+
<span>KG + PPR</span>
|
|
21
|
+
<span>1168 Tests</span>
|
|
21
22
|
</div>
|
|
22
23
|
</header>
|
|
23
24
|
|
|
24
25
|
<section class="brand-strip">
|
|
25
26
|
<button class="brand-card quick-card" data-quick-action="search">
|
|
26
27
|
<strong>Search</strong>
|
|
27
|
-
<span>
|
|
28
|
+
<span>hybrid recall</span>
|
|
28
29
|
</button>
|
|
29
30
|
<button class="brand-card quick-card" data-quick-action="distill">
|
|
30
31
|
<strong>Distill</strong>
|
|
31
32
|
<span>build a briefing</span>
|
|
32
33
|
</button>
|
|
34
|
+
<button class="brand-card quick-card" data-quick-action="skills">
|
|
35
|
+
<strong>Skills</strong>
|
|
36
|
+
<span>executable patterns</span>
|
|
37
|
+
</button>
|
|
33
38
|
<button class="brand-card quick-card" data-quick-action="pin">
|
|
34
39
|
<strong>Pin</strong>
|
|
35
40
|
<span>jump to pin flow</span>
|
|
@@ -37,14 +42,58 @@
|
|
|
37
42
|
</section>
|
|
38
43
|
|
|
39
44
|
<main class="workspace">
|
|
40
|
-
<section class="
|
|
45
|
+
<section id="dashboardView" class="dashboard-view card">
|
|
46
|
+
<div class="section-head">
|
|
47
|
+
<h2>Memory Dashboard</h2>
|
|
48
|
+
<button id="refreshDashboard" class="ghost-button">Refresh</button>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="dash-stats-row" id="dashStatsRow">
|
|
52
|
+
<div class="stat-card">
|
|
53
|
+
<span class="stat-value" id="dashTotal">-</span>
|
|
54
|
+
<span class="stat-label">Total Memories</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="stat-card">
|
|
57
|
+
<span class="stat-value" id="dashHealth">-</span>
|
|
58
|
+
<span class="stat-label">Health Score</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="stat-card">
|
|
61
|
+
<span class="stat-value" id="dashWeek">-</span>
|
|
62
|
+
<span class="stat-label">This Week</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="stat-card">
|
|
65
|
+
<span class="stat-value" id="dashMonth">-</span>
|
|
66
|
+
<span class="stat-label">This Month</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="dash-grid">
|
|
71
|
+
<div class="dash-panel">
|
|
72
|
+
<h3>Category Distribution</h3>
|
|
73
|
+
<div id="dashCategoryBars" class="category-bars"></div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="dash-panel">
|
|
76
|
+
<h3>Lint Summary</h3>
|
|
77
|
+
<div id="dashLintSummary" class="lint-summary">Loading...</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div class="dash-panel full-width">
|
|
82
|
+
<h3>Stale Memories <span id="dashStaleCount" class="badge">0</span></h3>
|
|
83
|
+
<div id="dashStaleList" class="stale-list">Loading...</div>
|
|
84
|
+
</div>
|
|
85
|
+
</section>
|
|
86
|
+
|
|
87
|
+
<section class="control-panel card is-hidden" id="searchView">
|
|
41
88
|
<div class="section-head">
|
|
42
89
|
<h2>Query Console</h2>
|
|
43
90
|
<button id="statsButton" class="ghost-button">Refresh Stats</button>
|
|
44
91
|
</div>
|
|
45
92
|
|
|
46
93
|
<div class="view-switch" id="viewSwitch">
|
|
47
|
-
<button class="view-tab is-active" data-view="
|
|
94
|
+
<button class="view-tab is-active" data-view="dashboard">Dashboard</button>
|
|
95
|
+
<button class="view-tab" data-view="search">Search</button>
|
|
96
|
+
<button class="view-tab" data-view="skills">Skills</button>
|
|
48
97
|
<button class="view-tab" data-view="pins">Assets</button>
|
|
49
98
|
<button class="view-tab" data-view="exports">Exports</button>
|
|
50
99
|
</div>
|
|
@@ -70,7 +119,28 @@
|
|
|
70
119
|
</label>
|
|
71
120
|
</div>
|
|
72
121
|
|
|
73
|
-
<div class="grid
|
|
122
|
+
<div class="grid three-up">
|
|
123
|
+
<label class="field">
|
|
124
|
+
<span>Topic Tag</span>
|
|
125
|
+
<select id="topicTagInput">
|
|
126
|
+
<option value="">all topics</option>
|
|
127
|
+
<option value="auth">auth</option>
|
|
128
|
+
<option value="deploy">deploy</option>
|
|
129
|
+
<option value="infra">infra</option>
|
|
130
|
+
<option value="testing">testing</option>
|
|
131
|
+
<option value="database">database</option>
|
|
132
|
+
<option value="api">api</option>
|
|
133
|
+
<option value="ui">ui</option>
|
|
134
|
+
<option value="perf">perf</option>
|
|
135
|
+
<option value="security">security</option>
|
|
136
|
+
<option value="memory">memory</option>
|
|
137
|
+
<option value="mcp">mcp</option>
|
|
138
|
+
<option value="llm">llm</option>
|
|
139
|
+
<option value="config">config</option>
|
|
140
|
+
<option value="data">data</option>
|
|
141
|
+
<option value="docs">docs</option>
|
|
142
|
+
</select>
|
|
143
|
+
</label>
|
|
74
144
|
<label class="field">
|
|
75
145
|
<span>Limit</span>
|
|
76
146
|
<input id="limitInput" type="number" value="5" min="1" max="20" />
|
|
@@ -95,7 +165,7 @@
|
|
|
95
165
|
<p id="statusLine" class="status-line">Ready.</p>
|
|
96
166
|
</section>
|
|
97
167
|
|
|
98
|
-
<section class="result-panel card">
|
|
168
|
+
<section class="result-panel card is-hidden" id="resultView">
|
|
99
169
|
<div class="section-head">
|
|
100
170
|
<h2 id="resultTitle">Result Surface</h2>
|
|
101
171
|
<button id="reloadPinsButton" class="ghost-button">Load Assets</button>
|
|
@@ -113,7 +183,7 @@
|
|
|
113
183
|
<div id="resultCards" class="result-cards"></div>
|
|
114
184
|
</section>
|
|
115
185
|
|
|
116
|
-
<section class="side-panel card">
|
|
186
|
+
<section class="side-panel card is-hidden" id="sideView">
|
|
117
187
|
<div class="section-head">
|
|
118
188
|
<h2>Recall Actions</h2>
|
|
119
189
|
</div>
|
package/assets/ui/styles.css
CHANGED
|
@@ -170,7 +170,7 @@ h2, h3 {
|
|
|
170
170
|
|
|
171
171
|
.view-switch {
|
|
172
172
|
display: grid;
|
|
173
|
-
grid-template-columns: repeat(
|
|
173
|
+
grid-template-columns: repeat(4, 1fr);
|
|
174
174
|
gap: 8px;
|
|
175
175
|
margin-bottom: 14px;
|
|
176
176
|
}
|
|
@@ -206,6 +206,11 @@ textarea { resize: vertical; min-height: 120px; }
|
|
|
206
206
|
grid-template-columns: 1fr 1fr;
|
|
207
207
|
gap: 12px;
|
|
208
208
|
}
|
|
209
|
+
.grid.three-up {
|
|
210
|
+
display: grid;
|
|
211
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
212
|
+
gap: 12px;
|
|
213
|
+
}
|
|
209
214
|
|
|
210
215
|
.action-row {
|
|
211
216
|
display: flex;
|
|
@@ -595,7 +600,7 @@ textarea { resize: vertical; min-height: 120px; }
|
|
|
595
600
|
width: min(100vw - 18px, 100%);
|
|
596
601
|
padding-top: 16px;
|
|
597
602
|
}
|
|
598
|
-
.grid.two-up {
|
|
603
|
+
.grid.two-up, .grid.three-up {
|
|
599
604
|
grid-template-columns: 1fr;
|
|
600
605
|
}
|
|
601
606
|
.card {
|
|
@@ -603,3 +608,174 @@ textarea { resize: vertical; min-height: 120px; }
|
|
|
603
608
|
padding: 16px;
|
|
604
609
|
}
|
|
605
610
|
}
|
|
611
|
+
|
|
612
|
+
.is-hidden {
|
|
613
|
+
display: none !important;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* ---------------------------------------------------------------------------
|
|
617
|
+
Dashboard
|
|
618
|
+
--------------------------------------------------------------------------- */
|
|
619
|
+
|
|
620
|
+
.dashboard-view {
|
|
621
|
+
grid-column: 1 / -1;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.dash-stats-row {
|
|
625
|
+
display: grid;
|
|
626
|
+
grid-template-columns: repeat(4, 1fr);
|
|
627
|
+
gap: 1rem;
|
|
628
|
+
margin-bottom: 1.5rem;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.stat-card {
|
|
632
|
+
display: flex;
|
|
633
|
+
flex-direction: column;
|
|
634
|
+
align-items: center;
|
|
635
|
+
padding: 1.25rem;
|
|
636
|
+
border-radius: 0.75rem;
|
|
637
|
+
background: rgba(29, 42, 43, 0.05);
|
|
638
|
+
border: 1px solid var(--line);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.stat-value {
|
|
642
|
+
font-size: 2rem;
|
|
643
|
+
font-weight: 700;
|
|
644
|
+
font-variant-numeric: tabular-nums;
|
|
645
|
+
line-height: 1.2;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.stat-value.good { color: #10b981; }
|
|
649
|
+
.stat-value.warn { color: #f59e0b; }
|
|
650
|
+
.stat-value.bad { color: #ef4444; }
|
|
651
|
+
|
|
652
|
+
.stat-label {
|
|
653
|
+
font-size: 0.8rem;
|
|
654
|
+
color: var(--muted);
|
|
655
|
+
margin-top: 0.25rem;
|
|
656
|
+
text-transform: uppercase;
|
|
657
|
+
letter-spacing: 0.05em;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.dash-grid {
|
|
661
|
+
display: grid;
|
|
662
|
+
grid-template-columns: 1fr 1fr;
|
|
663
|
+
gap: 1.5rem;
|
|
664
|
+
margin-bottom: 1.5rem;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.dash-panel {
|
|
668
|
+
padding: 1rem;
|
|
669
|
+
border-radius: 0.75rem;
|
|
670
|
+
background: rgba(29, 42, 43, 0.05);
|
|
671
|
+
border: 1px solid var(--line);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
.dash-panel.full-width {
|
|
675
|
+
grid-column: 1 / -1;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
.dash-panel h3 {
|
|
679
|
+
font-size: 0.85rem;
|
|
680
|
+
font-weight: 600;
|
|
681
|
+
margin-bottom: 0.75rem;
|
|
682
|
+
color: var(--ink);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.badge {
|
|
686
|
+
display: inline-block;
|
|
687
|
+
background: rgba(29, 42, 43, 0.08);
|
|
688
|
+
color: var(--muted);
|
|
689
|
+
padding: 0.1em 0.5em;
|
|
690
|
+
border-radius: 999px;
|
|
691
|
+
font-size: 0.75rem;
|
|
692
|
+
font-weight: 500;
|
|
693
|
+
margin-left: 0.5rem;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/* Category bar chart */
|
|
697
|
+
.category-bars {
|
|
698
|
+
display: flex;
|
|
699
|
+
flex-direction: column;
|
|
700
|
+
gap: 0.5rem;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.bar-row {
|
|
704
|
+
display: grid;
|
|
705
|
+
grid-template-columns: 5.5rem 1fr 2.5rem;
|
|
706
|
+
align-items: center;
|
|
707
|
+
gap: 0.5rem;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.bar-label {
|
|
711
|
+
font-size: 0.8rem;
|
|
712
|
+
color: var(--muted);
|
|
713
|
+
text-align: right;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
.bar-track {
|
|
717
|
+
height: 1.25rem;
|
|
718
|
+
background: rgba(29, 42, 43, 0.06);
|
|
719
|
+
border-radius: 0.25rem;
|
|
720
|
+
overflow: hidden;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
.bar-fill {
|
|
724
|
+
height: 100%;
|
|
725
|
+
border-radius: 0.25rem;
|
|
726
|
+
transition: width 0.4s ease;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
.bar-count {
|
|
730
|
+
font-size: 0.8rem;
|
|
731
|
+
color: var(--muted);
|
|
732
|
+
font-variant-numeric: tabular-nums;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* Lint summary */
|
|
736
|
+
.lint-summary {
|
|
737
|
+
display: flex;
|
|
738
|
+
flex-direction: column;
|
|
739
|
+
gap: 0.5rem;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.lint-ok {
|
|
743
|
+
color: var(--accent);
|
|
744
|
+
font-size: 0.85rem;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.lint-issue {
|
|
748
|
+
font-size: 0.85rem;
|
|
749
|
+
padding: 0.35rem 0;
|
|
750
|
+
border-bottom: 1px solid var(--line);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.lint-issue:last-child {
|
|
754
|
+
border-bottom: none;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/* Stale list */
|
|
758
|
+
.stale-list {
|
|
759
|
+
max-height: 300px;
|
|
760
|
+
overflow-y: auto;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.stale-item {
|
|
764
|
+
font-size: 0.8rem;
|
|
765
|
+
color: var(--muted);
|
|
766
|
+
padding: 0.35rem 0;
|
|
767
|
+
border-bottom: 1px solid var(--line);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.stale-item:last-child {
|
|
771
|
+
border-bottom: none;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
@media (max-width: 768px) {
|
|
775
|
+
.dash-stats-row {
|
|
776
|
+
grid-template-columns: repeat(2, 1fr);
|
|
777
|
+
}
|
|
778
|
+
.dash-grid {
|
|
779
|
+
grid-template-columns: 1fr;
|
|
780
|
+
}
|
|
781
|
+
}
|