opencode-rag-plugin 1.18.0 → 1.18.1
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 +41 -1
- package/dist/cli/commands/init-helpers.d.ts +5 -1
- package/dist/cli/commands/init-helpers.js +8 -19
- package/dist/cli/commands/init.js +42 -8
- package/dist/cli/commands/setup.js +15 -6
- package/dist/cli/commands/ui.js +1 -1
- package/dist/core/config.d.ts +10 -0
- package/dist/core/config.js +6 -1
- package/dist/core/interfaces.d.ts +9 -0
- package/dist/core/runtime-overrides.d.ts +7 -0
- package/dist/core/runtime-overrides.js +15 -0
- package/dist/describer/anthropic.d.ts +4 -0
- package/dist/describer/anthropic.js +9 -2
- package/dist/describer/describer.d.ts +4 -0
- package/dist/describer/describer.js +8 -0
- package/dist/describer/gemini.d.ts +3 -0
- package/dist/describer/gemini.js +8 -2
- package/dist/indexer/pipeline.js +40 -0
- package/dist/opencode/system-guidance.d.ts +34 -0
- package/dist/opencode/system-guidance.js +127 -0
- package/dist/plugin.js +74 -35
- package/dist/quirks/auto-capture.d.ts +14 -0
- package/dist/quirks/auto-capture.js +112 -0
- package/dist/quirks/prompts.d.ts +1 -0
- package/dist/quirks/prompts.js +13 -0
- package/dist/quirks/quirk-store.d.ts +2 -0
- package/dist/quirks/quirk-store.js +1 -1
- package/dist/vectorstore/lancedb.d.ts +10 -1
- package/dist/vectorstore/lancedb.js +28 -2
- package/dist/vectorstore/memory.d.ts +2 -0
- package/dist/vectorstore/memory.js +3 -0
- package/dist/web/api.d.ts +3 -1
- package/dist/web/api.js +50 -1
- package/dist/web/server.d.ts +2 -1
- package/dist/web/server.js +3 -2
- package/dist/web/ui/index.html +163 -0
- package/package.json +1 -1
package/dist/web/server.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface WebUiServer {
|
|
|
15
15
|
* @param port - TCP port to listen on.
|
|
16
16
|
* @param cwd - Optional workspace root used to resolve file paths for the file API.
|
|
17
17
|
* @param vectorDimension - Embedding vector dimension (default 384).
|
|
18
|
+
* @param cfg - Active RAG configuration (used by quirk endpoints).
|
|
18
19
|
* @returns A {@link WebUiServer} handle for the running server.
|
|
19
20
|
*/
|
|
20
|
-
export declare function startWebUi(storePath: string, port: number, cwd?: string, vectorDimension?: number): Promise<WebUiServer>;
|
|
21
|
+
export declare function startWebUi(storePath: string, port: number, cwd?: string, vectorDimension?: number, cfg?: import("../core/config.js").RagConfig): Promise<WebUiServer>;
|
package/dist/web/server.js
CHANGED
|
@@ -53,13 +53,14 @@ function serveUiAsset(res, filePath) {
|
|
|
53
53
|
* @param port - TCP port to listen on.
|
|
54
54
|
* @param cwd - Optional workspace root used to resolve file paths for the file API.
|
|
55
55
|
* @param vectorDimension - Embedding vector dimension (default 384).
|
|
56
|
+
* @param cfg - Active RAG configuration (used by quirk endpoints).
|
|
56
57
|
* @returns A {@link WebUiServer} handle for the running server.
|
|
57
58
|
*/
|
|
58
|
-
export async function startWebUi(storePath, port, cwd, vectorDimension = 384) {
|
|
59
|
+
export async function startWebUi(storePath, port, cwd, vectorDimension = 384, cfg) {
|
|
59
60
|
const store = new LanceDbStore(storePath, vectorDimension);
|
|
60
61
|
const keywordIndex = await KeywordIndex.load(storePath);
|
|
61
62
|
const html = getStaticHtml();
|
|
62
|
-
const apiHandler = createApiHandler(store, keywordIndex, storePath, cwd);
|
|
63
|
+
const apiHandler = createApiHandler(store, keywordIndex, storePath, cwd, cfg);
|
|
63
64
|
const server = createServer(async (req, res) => {
|
|
64
65
|
const url = req.url ?? "/";
|
|
65
66
|
if (url === "/" || url === "/index.html") {
|
package/dist/web/ui/index.html
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
<button id="nav-chunks" class="nav-btn px-3 py-1 rounded text-sm font-medium hover:bg-slate-700 transition">Chunks</button>
|
|
25
25
|
<button id="nav-files" class="nav-btn px-3 py-1 rounded text-sm font-medium hover:bg-slate-700 transition">Files</button>
|
|
26
26
|
<button id="nav-evaluate" class="nav-btn px-3 py-1 rounded text-sm font-medium hover:bg-slate-700 transition">Evaluate</button>
|
|
27
|
+
<button id="nav-quirks" class="nav-btn px-3 py-1 rounded text-sm font-medium hover:bg-slate-700 transition">Quirks</button>
|
|
27
28
|
</nav>
|
|
28
29
|
<div class="flex-1"></div>
|
|
29
30
|
<div class="relative">
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
</div>
|
|
57
58
|
<div id="view-files" class="h-full overflow-y-auto scrollbar-thin"></div>
|
|
58
59
|
<div id="view-evaluate" class="hidden h-full flex flex-col"></div>
|
|
60
|
+
<div id="view-quirks" class="hidden h-full overflow-y-auto scrollbar-thin"></div>
|
|
59
61
|
</main>
|
|
60
62
|
</div>
|
|
61
63
|
|
|
@@ -85,6 +87,9 @@
|
|
|
85
87
|
async evalAnalysis(id) { const r = await fetch('/api/eval/sessions/' + encodeURIComponent(id) + '/analysis'); return r.json(); },
|
|
86
88
|
async evalTokenCompare(a, b) { const p = new URLSearchParams({a, b}); const r = await fetch('/api/eval/token-compare?' + p); return r.json(); },
|
|
87
89
|
async evalProjectSavings(params) { const r = await fetch('/api/eval/project-savings', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(params) }); return r.json(); },
|
|
90
|
+
async quirks() { const r = await fetch('/api/quirks'); return r.json(); },
|
|
91
|
+
async quirkLint() { const r = await fetch('/api/quirks/lint'); return r.json(); },
|
|
92
|
+
async deleteQuirk(id) { const r = await fetch('/api/quirks/' + encodeURIComponent(id), { method: 'DELETE' }); return r.json(); },
|
|
88
93
|
};
|
|
89
94
|
|
|
90
95
|
const state = {
|
|
@@ -101,6 +106,8 @@
|
|
|
101
106
|
currentChunks: [],
|
|
102
107
|
evalSelectedSessions: new Set(),
|
|
103
108
|
evalSessionDetail: null,
|
|
109
|
+
|
|
110
|
+
quirkTypeFilter: null,
|
|
104
111
|
};
|
|
105
112
|
|
|
106
113
|
function $(sel) { return document.querySelector(sel); }
|
|
@@ -131,6 +138,21 @@
|
|
|
131
138
|
return `<span class="inline-block px-1.5 py-0.5 rounded text-xs font-mono ${langColor(lang)} bg-slate-800">${lang}</span>`;
|
|
132
139
|
}
|
|
133
140
|
|
|
141
|
+
function quirkTypeColor(quirkType) {
|
|
142
|
+
const map = {
|
|
143
|
+
gotcha: 'text-amber-400',
|
|
144
|
+
preference: 'text-emerald-400',
|
|
145
|
+
decision: 'text-sky-400',
|
|
146
|
+
'environment-constraint': 'text-rose-400',
|
|
147
|
+
};
|
|
148
|
+
return map[quirkType] || 'text-slate-400';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function quirkTypeBadge(quirkType) {
|
|
152
|
+
const label = quirkType || 'general';
|
|
153
|
+
return `<span class="inline-block px-1.5 py-0.5 rounded text-xs font-mono ${quirkTypeColor(quirkType)} bg-slate-800">${label}</span>`;
|
|
154
|
+
}
|
|
155
|
+
|
|
134
156
|
function truncate(s, len=80) {
|
|
135
157
|
if (!s) return '';
|
|
136
158
|
return s.length > len ? s.slice(0, len) + '...' : s;
|
|
@@ -794,6 +816,8 @@
|
|
|
794
816
|
|
|
795
817
|
$('#nav-evaluate')?.addEventListener('click', () => { showView('evaluate'); renderEvaluate(); });
|
|
796
818
|
|
|
819
|
+
$('#nav-quirks')?.addEventListener('click', () => { showView('quirks'); renderQuirks(); });
|
|
820
|
+
|
|
797
821
|
// ── Evaluate View ──────────────────────────────────────────────────
|
|
798
822
|
|
|
799
823
|
function formatTokens(n) {
|
|
@@ -1468,6 +1492,145 @@
|
|
|
1468
1492
|
}
|
|
1469
1493
|
}
|
|
1470
1494
|
|
|
1495
|
+
// ── Quirks View ────────────────────────────────────────────────────
|
|
1496
|
+
|
|
1497
|
+
function confidenceColor(c) {
|
|
1498
|
+
if (c >= 0.75) return 'text-green-400';
|
|
1499
|
+
if (c >= 0.5) return 'text-amber-400';
|
|
1500
|
+
return 'text-red-400';
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
function formatQuirkDate(iso) {
|
|
1504
|
+
if (!iso) return '-';
|
|
1505
|
+
const d = new Date(iso);
|
|
1506
|
+
if (isNaN(d.getTime())) return '-';
|
|
1507
|
+
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
async function renderQuirks() {
|
|
1511
|
+
const el = $('#view-quirks');
|
|
1512
|
+
if (!el) return;
|
|
1513
|
+
el.innerHTML = '<div class="text-center py-10 text-slate-500">Loading quirks...</div>';
|
|
1514
|
+
|
|
1515
|
+
let data;
|
|
1516
|
+
try {
|
|
1517
|
+
data = await API.quirks();
|
|
1518
|
+
} catch (err) {
|
|
1519
|
+
el.innerHTML = `<div class="text-center py-10 text-red-400">Failed to load quirks: ${escapeHtml(String(err))}</div>`;
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
const quirks = data.quirks || [];
|
|
1524
|
+
const types = [...new Set(quirks.map(q => q.quirkType || 'general'))].sort();
|
|
1525
|
+
const filter = state.quirkTypeFilter;
|
|
1526
|
+
|
|
1527
|
+
const filtered = filter ? quirks.filter(q => (q.quirkType || 'general') === filter) : quirks;
|
|
1528
|
+
|
|
1529
|
+
if (quirks.length === 0) {
|
|
1530
|
+
el.innerHTML = `
|
|
1531
|
+
<div class="text-center flex-1 flex items-center justify-center py-20">
|
|
1532
|
+
<div>
|
|
1533
|
+
<div class="text-4xl mb-3">💡</div>
|
|
1534
|
+
<h2 class="text-lg font-semibold text-slate-400 mb-2">No quirks stored yet</h2>
|
|
1535
|
+
<p class="text-sm text-slate-500 max-w-md mx-auto">
|
|
1536
|
+
Quirks are experiential memories (gotchas, preferences, decisions, environment constraints)
|
|
1537
|
+
recorded by the agent via the <code>add_quirk</code> tool. They will appear here once captured.
|
|
1538
|
+
</p>
|
|
1539
|
+
</div>
|
|
1540
|
+
</div>`;
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
el.innerHTML = `
|
|
1545
|
+
<div class="flex items-center gap-3 mb-4">
|
|
1546
|
+
<h2 class="text-lg font-semibold text-white">Quirks</h2>
|
|
1547
|
+
<span class="text-sm text-slate-400">${quirks.length} total${filter ? `, ${filtered.length} shown` : ''}</span>
|
|
1548
|
+
<div class="flex-1"></div>
|
|
1549
|
+
<div class="flex items-center gap-2">
|
|
1550
|
+
<button class="quirk-filter px-2 py-1 rounded text-xs ${!filter ? 'bg-brand-600 text-white' : 'bg-slate-700 text-slate-300 hover:bg-slate-600'}" data-type="">All</button>
|
|
1551
|
+
${types.map(t => `
|
|
1552
|
+
<button class="quirk-filter px-2 py-1 rounded text-xs ${filter === t ? 'bg-brand-600 text-white' : 'bg-slate-700 text-slate-300 hover:bg-slate-600'}" data-type="${escapeHtml(t)}">${escapeHtml(t)}</button>
|
|
1553
|
+
`).join('')}
|
|
1554
|
+
</div>
|
|
1555
|
+
<button id="quirk-lint-btn" class="px-3 py-1 bg-slate-700 rounded text-sm hover:bg-slate-600">Lint</button>
|
|
1556
|
+
</div>
|
|
1557
|
+
<div id="quirk-lint-area" class="hidden mb-4"></div>
|
|
1558
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
|
|
1559
|
+
${filtered.map(q => `
|
|
1560
|
+
<div class="bg-slate-900 rounded-lg border border-slate-700 p-3 flex flex-col" data-id="${escapeHtml(q.id)}">
|
|
1561
|
+
<div class="flex items-center gap-2 mb-2">
|
|
1562
|
+
${quirkTypeBadge(q.quirkType)}
|
|
1563
|
+
<span class="text-xs text-slate-500">confidence <span class="${confidenceColor(q.confidence)}">${((q.confidence ?? 1) * 100).toFixed(0)}%</span></span>
|
|
1564
|
+
<span class="text-xs text-slate-600 ml-auto">${formatQuirkDate(q.lastObserved)}</span>
|
|
1565
|
+
</div>
|
|
1566
|
+
<p class="text-sm text-slate-200 whitespace-pre-wrap flex-1">${escapeHtml(q.content)}</p>
|
|
1567
|
+
${q.tags && q.tags.length ? `
|
|
1568
|
+
<div class="flex flex-wrap gap-1 mt-2">
|
|
1569
|
+
${q.tags.map(t => `<span class="inline-block px-1.5 py-0.5 rounded text-xs bg-slate-800 text-slate-400">#${escapeHtml(t)}</span>`).join('')}
|
|
1570
|
+
</div>` : ''}
|
|
1571
|
+
${q.sourceRef ? `<div class="text-xs text-slate-600 mt-2 font-mono">src: ${escapeHtml(q.sourceRef)}</div>` : ''}
|
|
1572
|
+
<div class="flex items-center justify-between mt-2 pt-2 border-t border-slate-800">
|
|
1573
|
+
<span class="text-xs text-slate-600 font-mono">${escapeHtml(q.id)}</span>
|
|
1574
|
+
<button class="quirk-delete text-xs text-slate-600 hover:text-red-400" data-id="${escapeHtml(q.id)}">Delete</button>
|
|
1575
|
+
</div>
|
|
1576
|
+
</div>
|
|
1577
|
+
`).join('')}
|
|
1578
|
+
</div>
|
|
1579
|
+
`;
|
|
1580
|
+
|
|
1581
|
+
$$('.quirk-filter').forEach(btn => {
|
|
1582
|
+
btn.addEventListener('click', () => {
|
|
1583
|
+
state.quirkTypeFilter = btn.dataset.type || null;
|
|
1584
|
+
renderQuirks();
|
|
1585
|
+
});
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1588
|
+
$$('.quirk-delete').forEach(btn => {
|
|
1589
|
+
btn.addEventListener('click', async (e) => {
|
|
1590
|
+
e.stopPropagation();
|
|
1591
|
+
const id = btn.dataset.id;
|
|
1592
|
+
if (!confirm('Delete this quirk? This cannot be undone.')) return;
|
|
1593
|
+
btn.textContent = 'Deleting...';
|
|
1594
|
+
try {
|
|
1595
|
+
await API.deleteQuirk(id);
|
|
1596
|
+
renderQuirks();
|
|
1597
|
+
} catch (err) {
|
|
1598
|
+
alert('Failed to delete quirk: ' + String(err));
|
|
1599
|
+
renderQuirks();
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1604
|
+
$('#quirk-lint-btn')?.addEventListener('click', async () => {
|
|
1605
|
+
const area = $('#quirk-lint-area');
|
|
1606
|
+
if (!area) return;
|
|
1607
|
+
if (!area.classList.contains('hidden')) {
|
|
1608
|
+
area.classList.add('hidden');
|
|
1609
|
+
area.innerHTML = '';
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
area.classList.remove('hidden');
|
|
1613
|
+
area.innerHTML = '<div class="text-center py-3 text-slate-500">Running lint...</div>';
|
|
1614
|
+
try {
|
|
1615
|
+
const res = await API.quirkLint();
|
|
1616
|
+
const issues = res.issues || [];
|
|
1617
|
+
if (issues.length === 0) {
|
|
1618
|
+
area.innerHTML = `<div class="bg-green-900/20 border border-green-800 rounded-lg p-3 text-sm text-green-300">All quirks look healthy. No issues found.</div>`;
|
|
1619
|
+
} else {
|
|
1620
|
+
area.innerHTML = `
|
|
1621
|
+
<div class="bg-slate-900 rounded-lg border border-amber-800 p-3">
|
|
1622
|
+
<div class="text-sm font-semibold text-amber-300 mb-2">${issues.length} issue(s) found</div>
|
|
1623
|
+
<ul class="space-y-1 text-xs text-slate-300 list-disc list-inside">
|
|
1624
|
+
${issues.map(i => `<li>${escapeHtml(i)}</li>`).join('')}
|
|
1625
|
+
</ul>
|
|
1626
|
+
</div>`;
|
|
1627
|
+
}
|
|
1628
|
+
} catch (err) {
|
|
1629
|
+
area.innerHTML = `<div class="bg-red-900/20 border border-red-800 rounded-lg p-3 text-sm text-red-300">Lint failed: ${escapeHtml(String(err))}</div>`;
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1471
1634
|
// Init
|
|
1472
1635
|
renderDashboard();
|
|
1473
1636
|
</script>
|