@vheins/local-memory-mcp 0.2.0 → 0.3.12
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/dist/dashboard/public/app.js +358 -31
- package/dist/dashboard/public/index.html +131 -7
- package/dist/dashboard/server.js +109 -5
- package/dist/dashboard/server.js.map +1 -1
- package/dist/prompts/registry.d.ts +112 -0
- package/dist/prompts/registry.d.ts.map +1 -1
- package/dist/prompts/registry.js +172 -0
- package/dist/prompts/registry.js.map +1 -1
- package/dist/resources/index.test.js +2 -0
- package/dist/resources/index.test.js.map +1 -1
- package/dist/server.js +10 -10
- package/dist/server.js.map +1 -1
- package/dist/storage/sqlite.d.ts +6 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +91 -13
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/sqlite.test.js +2 -0
- package/dist/storage/sqlite.test.js.map +1 -1
- package/dist/storage/vectors.d.ts.map +1 -1
- package/dist/storage/vectors.js +0 -2
- package/dist/storage/vectors.js.map +1 -1
- package/dist/tools/memory.store.d.ts.map +1 -1
- package/dist/tools/memory.store.js +2 -0
- package/dist/tools/memory.store.js.map +1 -1
- package/dist/tools/memory.update.d.ts.map +1 -1
- package/dist/tools/memory.update.js +6 -0
- package/dist/tools/memory.update.js.map +1 -1
- package/dist/tools/schemas.d.ts +35 -1
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/schemas.js +21 -2
- package/dist/tools/schemas.js.map +1 -1
- package/dist/tools/task.manage.d.ts.map +1 -1
- package/dist/tools/task.manage.js +15 -2
- package/dist/tools/task.manage.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/logger.test.js +19 -27
- package/dist/utils/logger.test.js.map +1 -1
- package/dist/v2-features.test.js +2 -0
- package/dist/v2-features.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,6 +6,11 @@ let totalPages = 1;
|
|
|
6
6
|
let totalItems = 0;
|
|
7
7
|
let selectedIds = new Set();
|
|
8
8
|
let currentPaginatedData = [];
|
|
9
|
+
let taskPagination = {
|
|
10
|
+
todo: { page: 1, pageSize: 20, hasMore: true, loading: false },
|
|
11
|
+
in_progress: { page: 1, pageSize: 20, hasMore: true, loading: false },
|
|
12
|
+
completed: { page: 1, pageSize: 20, hasMore: true, loading: false }
|
|
13
|
+
};
|
|
9
14
|
let charts = {};
|
|
10
15
|
let lastSyncTime = Date.now();
|
|
11
16
|
let countdownSeconds = 30;
|
|
@@ -600,6 +605,21 @@ async function loadStats() {
|
|
|
600
605
|
document.getElementById('mistakeCount').textContent = data.byType?.mistake || 0;
|
|
601
606
|
document.getElementById('patternCount').textContent = data.byType?.pattern || 0;
|
|
602
607
|
|
|
608
|
+
// Fill Task stats
|
|
609
|
+
if (data.taskStats) {
|
|
610
|
+
document.getElementById('totalTasks').textContent = data.taskStats.total || 0;
|
|
611
|
+
document.getElementById('todoTasksCount').textContent = data.taskStats.todo || 0;
|
|
612
|
+
document.getElementById('inProgressTasksCount').textContent = data.taskStats.inProgress || 0;
|
|
613
|
+
document.getElementById('completedTasksCount').textContent = data.taskStats.completed || 0;
|
|
614
|
+
|
|
615
|
+
document.getElementById('todoStatCount').textContent = data.taskStats.todo || 0;
|
|
616
|
+
document.getElementById('inProgressStatCount').textContent = data.taskStats.inProgress || 0;
|
|
617
|
+
document.getElementById('completedStatCount').textContent = data.taskStats.completed || 0;
|
|
618
|
+
document.getElementById('blockedStatCount').textContent = data.taskStats.blocked || 0;
|
|
619
|
+
|
|
620
|
+
updateTaskStatusChart(data.taskStats);
|
|
621
|
+
}
|
|
622
|
+
|
|
603
623
|
updateTypeChart(data.byType);
|
|
604
624
|
updateTimeSeriesChart(data.timeSeries || {});
|
|
605
625
|
updateScatterChart(data.scatterData || []);
|
|
@@ -655,6 +675,51 @@ function updateTypeChart(byType) {
|
|
|
655
675
|
});
|
|
656
676
|
}
|
|
657
677
|
|
|
678
|
+
function updateTaskStatusChart(taskStats) {
|
|
679
|
+
const ctx = document.getElementById('taskStatusChart');
|
|
680
|
+
if (!window.Chart || !ctx) return;
|
|
681
|
+
if (charts.taskStatusChart) charts.taskStatusChart.destroy();
|
|
682
|
+
|
|
683
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
684
|
+
const counts = [
|
|
685
|
+
taskStats?.todo || 0,
|
|
686
|
+
taskStats?.inProgress || 0,
|
|
687
|
+
taskStats?.completed || 0,
|
|
688
|
+
taskStats?.blocked || 0
|
|
689
|
+
];
|
|
690
|
+
|
|
691
|
+
charts.taskStatusChart = new Chart(ctx, {
|
|
692
|
+
type: 'doughnut',
|
|
693
|
+
data: {
|
|
694
|
+
labels: ['To Do', 'In Progress', 'Completed', 'Blocked'],
|
|
695
|
+
datasets: [{
|
|
696
|
+
data: counts,
|
|
697
|
+
backgroundColor: ['#94a3b8', '#38bdf8', '#10b981', '#fb7185'],
|
|
698
|
+
borderWidth: 2,
|
|
699
|
+
borderColor: isDark ? '#1e293b' : '#ffffff',
|
|
700
|
+
hoverOffset: 12
|
|
701
|
+
}]
|
|
702
|
+
},
|
|
703
|
+
options: {
|
|
704
|
+
responsive: true,
|
|
705
|
+
maintainAspectRatio: false,
|
|
706
|
+
cutout: '68%',
|
|
707
|
+
plugins: {
|
|
708
|
+
legend: { display: false },
|
|
709
|
+
tooltip: {
|
|
710
|
+
backgroundColor: isDark ? '#1e293b' : '#ffffff',
|
|
711
|
+
titleColor: isDark ? '#f8fafc' : '#1e293b',
|
|
712
|
+
bodyColor: isDark ? '#94a3b8' : '#64748b',
|
|
713
|
+
borderColor: isDark ? '#334155' : '#e2e8f0',
|
|
714
|
+
borderWidth: 1,
|
|
715
|
+
padding: 10,
|
|
716
|
+
cornerRadius: 10
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
|
|
658
723
|
function updateTopMemoriesChart(memories = []) {
|
|
659
724
|
const ctx = document.getElementById('topMemoriesChart');
|
|
660
725
|
if (!window.Chart) {
|
|
@@ -1089,6 +1154,7 @@ function renderTable(memories) {
|
|
|
1089
1154
|
<td class="p-3">
|
|
1090
1155
|
<div class="flex flex-col">
|
|
1091
1156
|
<span class="text-[10px] font-bold text-sky-600 dark:text-sky-400 truncate max-w-[100px]" title="${m.agent || 'unknown'}">${m.agent || 'unknown'}</span>
|
|
1157
|
+
<span class="text-[9px] font-medium text-slate-500 dark:text-slate-400 truncate max-w-[100px]" title="${m.role || 'unknown'}">${m.role || 'unknown'}</span>
|
|
1092
1158
|
<span class="text-[9px] text-gray-400 dark:text-gray-500 truncate max-w-[100px]" title="${m.model || 'unknown'}">${m.model || 'unknown'}</span>
|
|
1093
1159
|
</div>
|
|
1094
1160
|
</td>
|
|
@@ -1589,47 +1655,101 @@ let currentTasks = [];
|
|
|
1589
1655
|
|
|
1590
1656
|
async function loadTasks() {
|
|
1591
1657
|
if (!currentRepo) return;
|
|
1658
|
+
|
|
1659
|
+
// Reset pagination
|
|
1660
|
+
taskPagination.todo = { page: 1, pageSize: 20, hasMore: true, loading: false };
|
|
1661
|
+
taskPagination.in_progress = { page: 1, pageSize: 20, hasMore: true, loading: false };
|
|
1662
|
+
taskPagination.completed = { page: 1, pageSize: 20, hasMore: true, loading: false };
|
|
1663
|
+
|
|
1664
|
+
// Clear containers
|
|
1665
|
+
document.getElementById('todoTasks').innerHTML = '';
|
|
1666
|
+
document.getElementById('inProgressTasks').innerHTML = '';
|
|
1667
|
+
document.getElementById('completedTasks').innerHTML = '';
|
|
1668
|
+
|
|
1669
|
+
await Promise.all([
|
|
1670
|
+
loadTaskCategory('pending,blocked,canceled'),
|
|
1671
|
+
loadTaskCategory('in_progress'),
|
|
1672
|
+
loadTaskCategory('completed')
|
|
1673
|
+
]);
|
|
1674
|
+
|
|
1675
|
+
setupTaskScrollListeners();
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
async function loadTaskCategory(status) {
|
|
1679
|
+
const category = (status.includes('pending') || status.includes('blocked') || status.includes('canceled')) ? 'todo' : (status === 'in_progress' ? 'in_progress' : 'completed');
|
|
1680
|
+
const pag = taskPagination[category];
|
|
1681
|
+
|
|
1682
|
+
if (!pag.hasMore || pag.loading) return;
|
|
1683
|
+
|
|
1684
|
+
pag.loading = true;
|
|
1685
|
+
const containerId = { todo: 'todoTasks', in_progress: 'inProgressTasks', completed: 'completedTasks' }[category];
|
|
1686
|
+
const container = document.getElementById(containerId);
|
|
1687
|
+
|
|
1688
|
+
// Show loading indicator
|
|
1689
|
+
const loadingId = `loading-${category}`;
|
|
1690
|
+
if (!document.getElementById(loadingId)) {
|
|
1691
|
+
const loader = document.createElement('div');
|
|
1692
|
+
loader.id = loadingId;
|
|
1693
|
+
loader.className = 'py-4 text-center text-gray-400 text-[10px] animate-pulse w-full';
|
|
1694
|
+
loader.textContent = 'Loading more...';
|
|
1695
|
+
container.appendChild(loader);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1592
1698
|
try {
|
|
1593
|
-
const response = await fetch(`/api/tasks?repo=${encodeURIComponent(currentRepo)}`);
|
|
1699
|
+
const response = await fetch(`/api/tasks?repo=${encodeURIComponent(currentRepo)}&status=${status}&page=${pag.page}&pageSize=${pag.pageSize}`);
|
|
1594
1700
|
const data = await response.json();
|
|
1595
|
-
|
|
1596
|
-
|
|
1701
|
+
|
|
1702
|
+
const tasks = data.tasks || [];
|
|
1703
|
+
|
|
1704
|
+
// Remove loader
|
|
1705
|
+
const loader = document.getElementById(loadingId);
|
|
1706
|
+
if (loader) loader.remove();
|
|
1707
|
+
|
|
1708
|
+
if (tasks.length < pag.pageSize) {
|
|
1709
|
+
pag.hasMore = false;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
renderTaskCards(containerId, tasks, pag.page === 1);
|
|
1713
|
+
pag.page++;
|
|
1597
1714
|
} catch (err) {
|
|
1598
|
-
console.error(
|
|
1715
|
+
console.error(`Failed to load ${category} tasks:`, err);
|
|
1716
|
+
} finally {
|
|
1717
|
+
pag.loading = false;
|
|
1599
1718
|
}
|
|
1600
1719
|
}
|
|
1601
1720
|
|
|
1602
|
-
function
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
renderTaskColumn('completedTasks', completedTasks);
|
|
1721
|
+
function setupTaskScrollListeners() {
|
|
1722
|
+
['todoTasks', 'inProgressTasks', 'completedTasks'].forEach(id => {
|
|
1723
|
+
const el = document.getElementById(id);
|
|
1724
|
+
if (!el) return;
|
|
1725
|
+
|
|
1726
|
+
el.onscroll = () => {
|
|
1727
|
+
if (el.scrollTop + el.clientHeight >= el.scrollHeight - 50) {
|
|
1728
|
+
const category = id === 'todoTasks' ? 'todo' : (id === 'inProgressTasks' ? 'in_progress' : 'completed');
|
|
1729
|
+
const status = category === 'todo' ? 'pending,blocked,canceled' : (category === 'in_progress' ? 'in_progress' : 'completed');
|
|
1730
|
+
loadTaskCategory(status);
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
});
|
|
1616
1734
|
}
|
|
1617
1735
|
|
|
1618
|
-
function
|
|
1619
|
-
const container = document.getElementById(
|
|
1736
|
+
function renderTaskCards(containerId, tasks, clear = false) {
|
|
1737
|
+
const container = document.getElementById(containerId);
|
|
1620
1738
|
if (!container) return;
|
|
1621
|
-
|
|
1622
|
-
if (!tasks || tasks.length === 0) {
|
|
1739
|
+
|
|
1740
|
+
if (clear && (!tasks || tasks.length === 0)) {
|
|
1623
1741
|
container.innerHTML = '<div class="text-center py-8 text-gray-400 text-xs italic bg-gray-50/50 dark:bg-gray-900/20 rounded-xl border border-dashed border-gray-200 dark:border-gray-700">No tasks</div>';
|
|
1624
1742
|
return;
|
|
1625
1743
|
}
|
|
1626
1744
|
|
|
1627
|
-
|
|
1745
|
+
const html = tasks.map(t => `
|
|
1628
1746
|
<div class="bg-white dark:bg-gray-700 p-4 rounded-xl shadow-sm border border-gray-100 dark:border-gray-600 hover:shadow-md transition-all group">
|
|
1629
1747
|
<div class="flex items-center justify-between mb-2">
|
|
1630
1748
|
<div class="flex items-center gap-2">
|
|
1631
1749
|
<span class="px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-800 text-[10px] font-bold text-gray-600 dark:text-gray-400 font-mono border border-gray-200 dark:border-gray-700">${t.task_code}</span>
|
|
1632
1750
|
<span class="text-[10px] font-bold uppercase tracking-wider text-gray-400">${t.phase}</span>
|
|
1751
|
+
${t.status === 'blocked' ? '<span class="px-1 py-0.5 rounded bg-red-500 text-white text-[8px] font-bold uppercase">Blocked</span>' : ''}
|
|
1752
|
+
${t.status === 'canceled' ? '<span class="px-1 py-0.5 rounded bg-slate-500 text-white text-[8px] font-bold uppercase">Canceled</span>' : ''}
|
|
1633
1753
|
</div>
|
|
1634
1754
|
<div class="flex items-center gap-1">
|
|
1635
1755
|
${t.priority >= 4 ? '<span class="w-2 h-2 rounded-full bg-red-500 animate-pulse"></span>' : ''}
|
|
@@ -1637,7 +1757,25 @@ function renderTaskColumn(id, tasks) {
|
|
|
1637
1757
|
</div>
|
|
1638
1758
|
</div>
|
|
1639
1759
|
<h4 class="font-bold text-sm text-gray-900 dark:text-gray-100 mb-1">${escapeHtml(t.title)}</h4>
|
|
1640
|
-
<p class="text-xs text-gray-500 dark:text-gray-400 line-clamp-2 mb-
|
|
1760
|
+
<p class="text-xs text-gray-500 dark:text-gray-400 line-clamp-2 mb-2">${escapeHtml(t.description || '')}</p>
|
|
1761
|
+
|
|
1762
|
+
${t.doc_path ? `
|
|
1763
|
+
<div class="mb-3">
|
|
1764
|
+
<a href="${t.doc_path.startsWith('http') ? t.doc_path : '#'}" target="_blank" class="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-[10px] text-slate-500 dark:text-gray-400 hover:text-sky-600 transition-colors">
|
|
1765
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg>
|
|
1766
|
+
<span class="truncate max-w-[150px]">${escapeHtml(t.doc_path)}</span>
|
|
1767
|
+
</a>
|
|
1768
|
+
</div>
|
|
1769
|
+
` : ''}
|
|
1770
|
+
|
|
1771
|
+
<div class="flex items-center gap-2 mb-3">
|
|
1772
|
+
<div class="px-1.5 py-0.5 rounded bg-sky-50 dark:bg-sky-900/30 border border-sky-100 dark:border-sky-800 flex items-center gap-1">
|
|
1773
|
+
<span class="text-[9px] font-bold text-sky-600 dark:text-sky-400">${escapeHtml(t.agent || 'unknown')}</span>
|
|
1774
|
+
<span class="text-[8px] text-sky-400 dark:text-sky-600">|</span>
|
|
1775
|
+
<span class="text-[9px] font-medium text-sky-500 dark:text-sky-500">${escapeHtml(t.role || 'unknown')}</span>
|
|
1776
|
+
</div>
|
|
1777
|
+
</div>
|
|
1778
|
+
|
|
1641
1779
|
${t.depends_on ? `
|
|
1642
1780
|
<div class="mt-2 pt-2 border-t border-gray-50 dark:border-gray-600 flex items-center gap-1.5">
|
|
1643
1781
|
<svg class="w-3 h-3 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path></svg>
|
|
@@ -1646,10 +1784,16 @@ function renderTaskColumn(id, tasks) {
|
|
|
1646
1784
|
` : ''}
|
|
1647
1785
|
<div class="mt-3 flex items-center justify-between">
|
|
1648
1786
|
<span class="text-[10px] font-mono text-gray-400">${t.id.substring(0, 8)}</span>
|
|
1649
|
-
<span class="text-[10px] text-gray-400">${formatDate(t.created_at)}</span>
|
|
1787
|
+
<span class="text-[10px] text-gray-400">${t.status === 'completed' && t.finished_at ? 'Done ' + formatDate(t.finished_at) : formatDate(t.created_at)}</span>
|
|
1650
1788
|
</div>
|
|
1651
1789
|
</div>
|
|
1652
1790
|
`).join('');
|
|
1791
|
+
|
|
1792
|
+
if (clear) {
|
|
1793
|
+
container.innerHTML = html;
|
|
1794
|
+
} else {
|
|
1795
|
+
container.insertAdjacentHTML('beforeend', html);
|
|
1796
|
+
}
|
|
1653
1797
|
}
|
|
1654
1798
|
|
|
1655
1799
|
function getPriorityColor(p) {
|
|
@@ -1665,25 +1809,27 @@ function switchTab(tab) {
|
|
|
1665
1809
|
const dashTab = document.getElementById('dashboardTabBtn');
|
|
1666
1810
|
const memTab = document.getElementById('memoriesTabBtn');
|
|
1667
1811
|
const taskTab = document.getElementById('tasksTabBtn');
|
|
1812
|
+
const refTab = document.getElementById('referenceTabBtn');
|
|
1668
1813
|
const indicator = document.getElementById('tabIndicator');
|
|
1669
|
-
|
|
1814
|
+
|
|
1670
1815
|
const dashContent = document.getElementById('dashboardContent');
|
|
1671
1816
|
const memContent = document.getElementById('memoriesContent');
|
|
1672
1817
|
const taskContent = document.getElementById('tasksContent');
|
|
1818
|
+
const refContent = document.getElementById('referenceContent');
|
|
1673
1819
|
|
|
1674
1820
|
currentTab = tab;
|
|
1675
1821
|
localStorage.setItem('activeTab', tab);
|
|
1676
1822
|
|
|
1677
|
-
const tabs = [dashTab, memTab, taskTab];
|
|
1678
|
-
const contents = [dashContent, memContent, taskContent];
|
|
1679
|
-
const targetId = tab + 'TabBtn';
|
|
1823
|
+
const tabs = [dashTab, memTab, taskTab, refTab];
|
|
1824
|
+
const contents = [dashContent, memContent, taskContent, refContent]; const targetId = tab + 'TabBtn';
|
|
1680
1825
|
const targetContentId = tab + 'Content';
|
|
1681
1826
|
|
|
1682
1827
|
// Update indicator
|
|
1683
1828
|
if (indicator) {
|
|
1684
1829
|
if (tab === 'dashboard') indicator.style.transform = 'translateX(0)';
|
|
1685
|
-
|
|
1686
|
-
|
|
1830
|
+
if (tab === 'memories') indicator.style.transform = 'translateX(100%)';
|
|
1831
|
+
if (tab === 'tasks') indicator.style.transform = 'translateX(200%)';
|
|
1832
|
+
if (tab === 'reference') indicator.style.transform = 'translateX(300%)';
|
|
1687
1833
|
}
|
|
1688
1834
|
|
|
1689
1835
|
// Update button states
|
|
@@ -1714,6 +1860,8 @@ function switchTab(tab) {
|
|
|
1714
1860
|
if (tab === 'tasks') loadTasks();
|
|
1715
1861
|
if (tab === 'dashboard') loadStats();
|
|
1716
1862
|
if (tab === 'memories') loadMemories();
|
|
1863
|
+
if (tab === 'reference') loadCapabilities();
|
|
1864
|
+
syncStickyOffsets();
|
|
1717
1865
|
}
|
|
1718
1866
|
|
|
1719
1867
|
window.loadTasks = loadTasks;
|
|
@@ -1767,6 +1915,185 @@ safeAddEventListener('repoCollapsedSummaryButton', 'click', () => {
|
|
|
1767
1915
|
|
|
1768
1916
|
window.addEventListener('resize', syncStickyOffsets);
|
|
1769
1917
|
|
|
1918
|
+
let currentCapabilities = { tools: [], resources: [], prompts: [] };
|
|
1919
|
+
|
|
1920
|
+
async function loadCapabilities() {
|
|
1921
|
+
const toolsList = document.getElementById('toolsList');
|
|
1922
|
+
const resourcesList = document.getElementById('resourcesList');
|
|
1923
|
+
const promptsList = document.getElementById('promptsList');
|
|
1924
|
+
|
|
1925
|
+
if (!toolsList) return;
|
|
1926
|
+
|
|
1927
|
+
toolsList.innerHTML = '<div class="text-center py-4 text-gray-400 text-xs">Loading capabilities...</div>';
|
|
1928
|
+
|
|
1929
|
+
try {
|
|
1930
|
+
const response = await fetch('/api/capabilities');
|
|
1931
|
+
const data = await response.json();
|
|
1932
|
+
|
|
1933
|
+
currentCapabilities.tools = Array.isArray(data.tools) ? data.tools : [];
|
|
1934
|
+
currentCapabilities.resources = Array.isArray(data.resources) ? data.resources : [];
|
|
1935
|
+
currentCapabilities.prompts = Array.isArray(data.prompts) ? data.prompts : [];
|
|
1936
|
+
|
|
1937
|
+
toolsList.innerHTML = currentCapabilities.tools.map(t => `
|
|
1938
|
+
<div onclick="showCapabilityDetail('tools', '${t.name}')" class="p-3 bg-slate-50 dark:bg-slate-900/50 border border-slate-100 dark:border-slate-800 rounded-lg cursor-pointer hover:border-sky-500/50 transition-all group">
|
|
1939
|
+
<div class="font-bold text-xs text-sky-600 dark:text-sky-400 mb-1 group-hover:text-sky-500">${escapeHtml(t.name)}</div>
|
|
1940
|
+
<p class="text-[10px] text-gray-500 dark:text-gray-400 line-clamp-2">${escapeHtml(t.description)}</p>
|
|
1941
|
+
</div>
|
|
1942
|
+
`).join('') || '<div class="text-center py-4 text-gray-400 text-xs">No tools available</div>';
|
|
1943
|
+
|
|
1944
|
+
resourcesList.innerHTML = currentCapabilities.resources.map(r => `
|
|
1945
|
+
<div onclick="showCapabilityDetail('resources', '${r.name}')" class="p-3 bg-slate-50 dark:bg-slate-900/50 border border-slate-100 dark:border-slate-800 rounded-lg cursor-pointer hover:border-indigo-500/50 transition-all group">
|
|
1946
|
+
<div class="font-bold text-xs text-indigo-600 dark:text-indigo-400 mb-1 group-hover:text-indigo-500">${escapeHtml(r.name)}</div>
|
|
1947
|
+
<p class="text-[10px] text-gray-500 dark:text-gray-400 line-clamp-2">${escapeHtml(r.description || 'No description')}</p>
|
|
1948
|
+
<div class="text-[8px] font-mono text-gray-400 mt-1 truncate">${escapeHtml(r.uri)}</div>
|
|
1949
|
+
</div>
|
|
1950
|
+
`).join('') || '<div class="text-center py-4 text-gray-400 text-xs">No resources available</div>';
|
|
1951
|
+
|
|
1952
|
+
promptsList.innerHTML = currentCapabilities.prompts.map(p => `
|
|
1953
|
+
<div onclick="showCapabilityDetail('prompts', '${p.name}')" class="p-3 bg-slate-50 dark:bg-slate-900/50 border border-slate-100 dark:border-slate-800 rounded-lg cursor-pointer hover:border-emerald-500/50 transition-all group">
|
|
1954
|
+
<div class="font-bold text-xs text-emerald-600 dark:text-emerald-400 mb-1 group-hover:text-emerald-500">${escapeHtml(p.name)}</div>
|
|
1955
|
+
<p class="text-[10px] text-gray-500 dark:text-gray-400 line-clamp-2">${escapeHtml(p.description || 'No description')}</p>
|
|
1956
|
+
</div>
|
|
1957
|
+
`).join('') || '<div class="text-center py-4 text-gray-400 text-xs">No prompts available</div>';
|
|
1958
|
+
|
|
1959
|
+
} catch (err) {
|
|
1960
|
+
console.error('Failed to load capabilities:', err);
|
|
1961
|
+
toolsList.innerHTML = `<div class="text-center py-4 text-red-400 text-xs">Error: ${err.message}</div>`;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
async function handleCsvImport(event) {
|
|
1966
|
+
const file = event.target.files[0];
|
|
1967
|
+
if (!file || !currentRepo) return;
|
|
1968
|
+
|
|
1969
|
+
const reader = new FileReader();
|
|
1970
|
+
reader.onload = async (e) => {
|
|
1971
|
+
const csvData = e.target.result;
|
|
1972
|
+
try {
|
|
1973
|
+
const response = await fetch('/api/tasks/import-csv', {
|
|
1974
|
+
method: 'POST',
|
|
1975
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1976
|
+
body: JSON.stringify({ repo: currentRepo, csvData })
|
|
1977
|
+
});
|
|
1978
|
+
const result = await response.json();
|
|
1979
|
+
if (result.success) {
|
|
1980
|
+
showToast(`Successfully imported ${result.count} tasks`, 'success');
|
|
1981
|
+
loadTasks();
|
|
1982
|
+
loadStats();
|
|
1983
|
+
} else {
|
|
1984
|
+
showToast(result.error || 'Failed to import CSV', 'error');
|
|
1985
|
+
}
|
|
1986
|
+
} catch (err) {
|
|
1987
|
+
showToast('Import failed: ' + err.message, 'error');
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
reader.readAsText(file);
|
|
1991
|
+
event.target.value = '';
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
function downloadCsvTemplate() {
|
|
1995
|
+
const headers = "task_code,phase,title,description,priority,status,agent,role,doc_path";
|
|
1996
|
+
const example = "TASK-001,research,Integrate CSV,Add import feature to dashboard,4,pending,Gemini CLI,expert,docs/tasks.md";
|
|
1997
|
+
const csv = `${headers}\n${example}`;
|
|
1998
|
+
|
|
1999
|
+
const blob = new Blob([csv], { type: 'text/csv' });
|
|
2000
|
+
const url = window.URL.createObjectURL(blob);
|
|
2001
|
+
const a = document.createElement('a');
|
|
2002
|
+
a.setAttribute('hidden', '');
|
|
2003
|
+
a.setAttribute('href', url);
|
|
2004
|
+
a.setAttribute('download', 'task_template.csv');
|
|
2005
|
+
document.body.appendChild(a);
|
|
2006
|
+
a.click();
|
|
2007
|
+
document.body.removeChild(a);
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
function showCapabilityDetail(type, name) {
|
|
2011
|
+
const item = currentCapabilities[type].find(i => i.name === name);
|
|
2012
|
+
if (!item) return;
|
|
2013
|
+
|
|
2014
|
+
const drawer = document.getElementById('memoryDrawer');
|
|
2015
|
+
const title = document.getElementById('drawerTitle');
|
|
2016
|
+
const body = document.getElementById('drawerBody');
|
|
2017
|
+
|
|
2018
|
+
title.textContent = `${type.charAt(0).toUpperCase() + type.slice(1)}: ${name}`;
|
|
2019
|
+
|
|
2020
|
+
let contentHtml = `
|
|
2021
|
+
<div class="space-y-6">
|
|
2022
|
+
<div class="p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
2023
|
+
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">Description</h4>
|
|
2024
|
+
<p class="text-sm text-slate-600 dark:text-slate-300">${escapeHtml(item.description || 'No description')}</p>
|
|
2025
|
+
</div>
|
|
2026
|
+
`;
|
|
2027
|
+
|
|
2028
|
+
if (type === 'tools' && item.inputSchema) {
|
|
2029
|
+
contentHtml += `
|
|
2030
|
+
<div class="p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
2031
|
+
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">Input Schema</h4>
|
|
2032
|
+
<pre class="text-[10px] font-mono text-sky-600 dark:text-sky-400 overflow-x-auto">${JSON.stringify(item.inputSchema, null, 2)}</pre>
|
|
2033
|
+
</div>
|
|
2034
|
+
`;
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
if (type === 'resources' && item.uri) {
|
|
2038
|
+
contentHtml += `
|
|
2039
|
+
<div class="p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
2040
|
+
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">URI</h4>
|
|
2041
|
+
<code class="text-xs font-mono text-indigo-600 dark:text-indigo-400">${escapeHtml(item.uri)}</code>
|
|
2042
|
+
</div>
|
|
2043
|
+
`;
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
if (type === 'prompts' && item.arguments) {
|
|
2047
|
+
contentHtml += `
|
|
2048
|
+
<div class="p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
2049
|
+
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">Arguments</h4>
|
|
2050
|
+
<div class="space-y-3">
|
|
2051
|
+
${item.arguments.map(arg => `
|
|
2052
|
+
<div class="border-l-2 border-emerald-500 pl-3 py-1">
|
|
2053
|
+
<div class="text-xs font-bold text-emerald-600 dark:text-emerald-400">${escapeHtml(arg.name)} ${arg.required ? '<span class="text-[8px] bg-emerald-500 text-white px-1 rounded">REQUIRED</span>' : ''}</div>
|
|
2054
|
+
<div class="text-[10px] text-gray-500 dark:text-gray-400">${escapeHtml(arg.description || '')}</div>
|
|
2055
|
+
</div>
|
|
2056
|
+
`).join('')}
|
|
2057
|
+
</div>
|
|
2058
|
+
</div>
|
|
2059
|
+
`;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
if (type === 'prompts' && item.messages) {
|
|
2063
|
+
contentHtml += `
|
|
2064
|
+
<div class="p-4 bg-slate-50 dark:bg-slate-900/50 rounded-xl border border-slate-100 dark:border-slate-800">
|
|
2065
|
+
<h4 class="text-xs font-bold text-slate-400 uppercase mb-2">Instructions</h4>
|
|
2066
|
+
<div class="space-y-4">
|
|
2067
|
+
${item.messages.map(msg => {
|
|
2068
|
+
const rawContent = typeof msg.content === 'string' ? msg.content : (msg.content?.text || '');
|
|
2069
|
+
const renderedMarkdown = window.marked ? window.marked.parse(rawContent) : escapeHtml(rawContent);
|
|
2070
|
+
return `
|
|
2071
|
+
<div class="space-y-1">
|
|
2072
|
+
<div class="text-[10px] font-bold uppercase tracking-wider text-slate-400">${escapeHtml(msg.role)}</div>
|
|
2073
|
+
<div class="p-4 bg-white dark:bg-slate-900 border border-slate-100 dark:border-slate-800 rounded-lg text-sm markdown-body">
|
|
2074
|
+
${renderedMarkdown}
|
|
2075
|
+
</div>
|
|
2076
|
+
</div>
|
|
2077
|
+
`;
|
|
2078
|
+
}).join('')}
|
|
2079
|
+
</div>
|
|
2080
|
+
</div>
|
|
2081
|
+
`;
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
contentHtml += `</div>`;
|
|
2085
|
+
body.innerHTML = contentHtml;
|
|
2086
|
+
|
|
2087
|
+
drawer.classList.remove('hidden');
|
|
2088
|
+
document.body.classList.add('drawer-open');
|
|
2089
|
+
setTimeout(() => document.getElementById('drawerAside').classList.remove('translate-x-full'), 10);
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
window.handleCsvImport = handleCsvImport;
|
|
2093
|
+
window.downloadCsvTemplate = downloadCsvTemplate;
|
|
2094
|
+
window.loadCapabilities = loadCapabilities;
|
|
2095
|
+
window.showCapabilityDetail = showCapabilityDetail;
|
|
2096
|
+
|
|
1770
2097
|
initTheme();
|
|
1771
2098
|
initRepoSidebarState();
|
|
1772
2099
|
initPinnedRepos();
|