opencode-fractal-memory 0.6.8 → 0.6.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.
@@ -93,32 +93,6 @@ export function queryNodes(scope) {
93
93
  db.close();
94
94
  return rows.map((r) => rowToNode(r));
95
95
  }
96
- export function queryPlaybooks(scope) {
97
- const db = openDb(scope);
98
- if (!db)
99
- return [];
100
- const rows = db.query(`
101
- SELECT id, label, content, metadata, times_used, usefulness_score
102
- FROM memory_nodes
103
- WHERE type = 'playbook' AND scope = ?
104
- ORDER BY label
105
- `).all(scope);
106
- db.close();
107
- return rows.map((r) => {
108
- const meta = r.metadata ? JSON.parse(r.metadata) : {};
109
- return {
110
- id: r.id,
111
- name: r.label || "",
112
- description: r.content || "",
113
- steps: meta.steps || [],
114
- triggers: meta.triggers || [],
115
- tags: meta.tags || [],
116
- executionCount: meta.executionCount ?? r.times_used ?? 0,
117
- avgDurationMs: meta.avgDurationMs ?? null,
118
- lastExecutedAt: meta.lastExecutedAt ?? null,
119
- };
120
- });
121
- }
122
96
  export function cosineSimilarity(a, b) {
123
97
  let dot = 0, normA = 0, normB = 0;
124
98
  for (let i = 0; i < a.length && i < b.length; i++) {
@@ -177,6 +151,7 @@ export const TYPE_SHAPES = {
177
151
  improvement: "sphere",
178
152
  howto: "sphere",
179
153
  skill: "icosahedron",
154
+ playbook: "torus",
180
155
  unknown: "sphere",
181
156
  };
182
157
  export const CUSTOM_TYPE_SHAPES = {
@@ -1,6 +1,6 @@
1
1
  import { memLog } from "../logging";
2
2
  import { generateEmbedding } from "../embeddings";
3
- import { queryNodes, queryPlaybooks, getAvailableScopes, extractLinks, computeStats, readProjectConfig, writeProjectConfig, rowToNode, withDb, jsonResponse, cosineSimilarity, } from "./helpers";
3
+ import { queryNodes, getAvailableScopes, extractLinks, computeStats, readProjectConfig, writeProjectConfig, rowToNode, withDb, jsonResponse, cosineSimilarity, } from "./helpers";
4
4
  function handleScopes() {
5
5
  return jsonResponse(getAvailableScopes());
6
6
  }
@@ -15,9 +15,6 @@ function handleStats(ctx) {
15
15
  const nodes = queryNodes(ctx.scope);
16
16
  return jsonResponse(computeStats(nodes));
17
17
  }
18
- function handlePlaybooks(ctx) {
19
- return jsonResponse(queryPlaybooks(ctx.scope));
20
- }
21
18
  function handleConfigGet() {
22
19
  return jsonResponse(readProjectConfig());
23
20
  }
@@ -189,7 +186,6 @@ export function registerRoutes(router) {
189
186
  router.get(/^\/api\/nodes$/, (_, ctx) => handleNodes(ctx));
190
187
  router.get(/^\/api\/links$/, (_, ctx) => handleLinks(ctx));
191
188
  router.get(/^\/api\/stats$/, (_, ctx) => handleStats(ctx));
192
- router.get(/^\/api\/playbooks$/, (_, ctx) => handlePlaybooks(ctx));
193
189
  router.get(/^\/api\/config$/, () => handleConfigGet());
194
190
  router.put(/^\/api\/config$/, (req) => handleConfigSave(req));
195
191
  router.post(/^\/api\/config$/, (req) => handleConfigSave(req));
@@ -45,3 +45,6 @@ export function stopManagementServer() {
45
45
  activeProcess = null;
46
46
  }
47
47
  }
48
+ process.on("exit", stopManagementServer);
49
+ process.on("SIGINT", stopManagementServer);
50
+ process.on("SIGTERM", stopManagementServer);
@@ -715,21 +715,12 @@ function setupEventListeners() {
715
715
  btn.classList.add("active");
716
716
  const tab = btn.dataset.tab;
717
717
  const visualizePanel = document.getElementById("visualize-panel");
718
- const playbooksPanel = document.getElementById("playbooks-panel");
719
718
  const settingsPanel = document.getElementById("settings-panel");
720
719
  if (visualizePanel) visualizePanel.classList.toggle("active", tab === "visualize");
721
- if (playbooksPanel) playbooksPanel.classList.toggle("active", tab === "playbooks");
722
720
  if (settingsPanel) settingsPanel.classList.toggle("active", tab === "settings");
723
721
  if (tab === "settings") loadSettings();
724
- if (tab === "playbooks") loadPlaybooks();
725
722
  });
726
723
  });
727
-
728
- // Playbook search input
729
- const pbSearch = document.getElementById("playbook-search-input");
730
- if (pbSearch) {
731
- pbSearch.addEventListener("input", () => renderPlaybooks(playbookData));
732
- }
733
724
  }
734
725
 
735
726
  // ==================== Data Loading ====================
@@ -1298,111 +1289,6 @@ async function performServerSearch(query) {
1298
1289
  }
1299
1290
  }
1300
1291
 
1301
- // ==================== Playbooks ====================
1302
-
1303
- let playbookData = [];
1304
-
1305
- async function loadPlaybooks() {
1306
- const container = document.getElementById('playbook-list');
1307
- container.innerHTML = '<div style="color:#888;font-size:12px;text-align:center;padding:20px;">Loading playbooks...</div>';
1308
-
1309
- try {
1310
- const res = await fetch(`/api/playbooks?scope=${currentScope}`);
1311
- if (!res.ok) {
1312
- container.innerHTML = '<div style="color:#f44;font-size:12px;text-align:center;padding:20px;">Failed to load playbooks</div>';
1313
- return;
1314
- }
1315
- playbookData = await res.json();
1316
- renderPlaybooks(playbookData);
1317
- } catch (e) {
1318
- container.innerHTML = `<div style="color:#f44;font-size:12px;text-align:center;padding:20px;">Error: ${e.message}</div>`;
1319
- }
1320
- }
1321
-
1322
- function renderPlaybooks(playbooks) {
1323
- const container = document.getElementById('playbook-list');
1324
- const searchQ = (document.getElementById('playbook-search-input').value || '').toLowerCase();
1325
-
1326
- const filtered = searchQ
1327
- ? playbooks.filter(p =>
1328
- p.name.toLowerCase().includes(searchQ) ||
1329
- p.description.toLowerCase().includes(searchQ) ||
1330
- (p.tags || []).some(t => t.toLowerCase().includes(searchQ))
1331
- )
1332
- : playbooks;
1333
-
1334
- if (filtered.length === 0) {
1335
- container.innerHTML = '<div style="color:#888;font-size:12px;text-align:center;padding:20px;">No playbooks found.</div>';
1336
- return;
1337
- }
1338
-
1339
- container.innerHTML = filtered.map(renderPlaybookCard).join('');
1340
-
1341
- // Wire delete buttons after render
1342
- container.querySelectorAll('.pb-delete-btn').forEach(btn => {
1343
- btn.addEventListener('click', () => deletePlaybook(btn.dataset.pbId));
1344
- });
1345
- }
1346
-
1347
- function renderPlaybookCard(pb) {
1348
- const tags = (pb.tags || []).map(t => `<span class="pb-tag">${t}</span>`).join('');
1349
- const steps = (pb.steps || []).map(s =>
1350
- `<div class="pb-step">
1351
- <span class="step-tool">${s.toolName}</span>
1352
- ${s.critical ? '<span class="step-critical">\u26a0</span>' : ''}
1353
- <span>${s.description}</span>
1354
- </div>`
1355
- ).join('');
1356
- const triggers = (pb.triggers || []).map(t => {
1357
- if (t.type === 'task_keyword') return `<div class="pb-trigger">\ud83d\udd11 ${(t.keywords || []).join(', ')}</div>`;
1358
- if (t.type === 'tool_sequence') return `<div class="pb-trigger">\ud83d\udd27 ${(t.pattern || []).join(' \u2192 ')}</div>`;
1359
- return `<div class="pb-trigger">\ud83d\udccb ${t.type}</div>`;
1360
- }).join('');
1361
-
1362
- return `<div class="playbook-card">
1363
- <div class="pb-header">
1364
- <div>
1365
- <div class="pb-name">${pb.name}</div>
1366
- <div class="pb-desc">${pb.description}</div>
1367
- </div>
1368
- <button class="pb-delete-btn" data-pb-id="${pb.id}">Delete</button>
1369
- </div>
1370
- <div class="pb-meta">
1371
- <span>\u26a1 ${pb.executionCount || 0} runs</span>
1372
- ${pb.avgDurationMs ? `<span>\u23f1 ${(pb.avgDurationMs / 1000).toFixed(1)}s avg</span>` : ''}
1373
- ${pb.lastExecutedAt ? `<span>\ud83d\udd50 ${new Date(pb.lastExecutedAt).toLocaleDateString()}</span>` : ''}
1374
- </div>
1375
- ${triggers ? `<div class="pb-triggers">${triggers}</div>` : ''}
1376
- <div class="pb-steps">${steps}</div>
1377
- ${tags ? `<div class="pb-tags">${tags}</div>` : ''}
1378
- </div>`;
1379
- }
1380
-
1381
- async function deletePlaybook(id) {
1382
- if (!confirm('Delete this playbook?')) return;
1383
- try {
1384
- const res = await fetch(`/api/playbooks/${id}?scope=${currentScope}`, { method: 'DELETE' });
1385
- if (res.ok) {
1386
- playbookData = playbookData.filter(p => p.id !== id);
1387
- renderPlaybooks(playbookData);
1388
- } else {
1389
- alert('Failed to delete playbook');
1390
- }
1391
- } catch (e) {
1392
- alert('Error: ' + e.message);
1393
- }
1394
- }
1395
-
1396
- // Playbook search input
1397
- document.addEventListener('DOMContentLoaded', () => {
1398
- const searchInput = document.getElementById('playbook-search-input');
1399
- if (searchInput) {
1400
- searchInput.addEventListener('input', () => {
1401
- renderPlaybooks(playbookData);
1402
- });
1403
- }
1404
- });
1405
-
1406
1292
  // ==================== Animation Loop ====================
1407
1293
 
1408
1294
  function animate() {
@@ -1413,19 +1299,6 @@ function animate() {
1413
1299
  // ==================== Settings ====================
1414
1300
 
1415
1301
  document.addEventListener('DOMContentLoaded', () => {
1416
- document.querySelectorAll('.tab-btn').forEach(btn => {
1417
- btn.addEventListener('click', () => {
1418
- document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
1419
- btn.classList.add('active');
1420
- const tab = btn.dataset.tab;
1421
- document.getElementById('visualize-panel').classList.toggle('active', tab === 'visualize');
1422
- document.getElementById('playbooks-panel').classList.toggle('active', tab === 'playbooks');
1423
- document.getElementById('settings-panel').classList.toggle('active', tab === 'settings');
1424
- if (tab === 'settings') loadSettings();
1425
- if (tab === 'playbooks') loadPlaybooks();
1426
- });
1427
- });
1428
-
1429
1302
  document.getElementById('save-config').addEventListener('click', saveSettings);
1430
1303
  });
1431
1304
 
@@ -182,44 +182,6 @@
182
182
  .save-btn:hover { background: rgba(100, 150, 255, 0.5); }
183
183
  .save-message { font-size: 11px; color: #4f4; margin-top: 8px; text-align: center; }
184
184
 
185
- /* Playbook styles */
186
- .playbook-card {
187
- background: rgba(255,255,255,0.03); border-radius: 8px; padding: 12px;
188
- margin-bottom: 10px; border: 1px solid rgba(255,255,255,0.08);
189
- transition: border-color 0.2s;
190
- }
191
- .playbook-card:hover { border-color: rgba(100,150,255,0.3); }
192
- .playbook-card .pb-header { display: flex; justify-content: space-between; align-items: flex-start; }
193
- .playbook-card .pb-name { font-size: 14px; color: #fff; font-weight: 500; }
194
- .playbook-card .pb-desc { font-size: 11px; color: #888; margin-top: 2px; }
195
- .playbook-card .pb-meta { font-size: 10px; color: #666; margin-top: 6px; }
196
- .playbook-card .pb-meta span { display: inline-block; margin-right: 12px; }
197
- .playbook-card .pb-tags { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; }
198
- .playbook-card .pb-tag {
199
- padding: 2px 8px; border-radius: 10px; background: rgba(100,150,255,0.15);
200
- color: rgba(100,150,255,0.8); font-size: 10px;
201
- }
202
- .playbook-card .pb-steps { margin-top: 8px; }
203
- .playbook-card .pb-step {
204
- padding: 4px 8px; margin: 2px 0; border-radius: 4px;
205
- background: rgba(255,255,255,0.03); font-size: 11px; color: #aaa;
206
- display: flex; align-items: center; gap: 6px;
207
- }
208
- .playbook-card .pb-step .step-tool {
209
- padding: 1px 6px; border-radius: 4px; background: rgba(100,150,255,0.2);
210
- color: #4a9eff; font-size: 10px; font-weight: 500;
211
- }
212
- .playbook-card .pb-step .step-critical {
213
- color: #f66; font-size: 10px; font-weight: 700;
214
- }
215
- .playbook-card .pb-delete-btn {
216
- background: none; border: 1px solid rgba(255,60,60,0.3); color: #f66;
217
- padding: 2px 10px; border-radius: 4px; font-size: 11px; cursor: pointer;
218
- }
219
- .playbook-card .pb-delete-btn:hover { background: rgba(255,60,60,0.2); }
220
- .playbook-card .pb-triggers { margin-top: 6px; }
221
- .playbook-card .pb-trigger { font-size: 10px; color: #888; font-style: italic; }
222
-
223
185
  #node-list {
224
186
  max-height: 40vh; overflow-y: auto; border: 1px solid rgba(255,255,255,0.1);
225
187
  border-radius: 6px;
@@ -245,7 +207,6 @@
245
207
 
246
208
  <div class="tab-buttons">
247
209
  <button class="tab-btn active" data-tab="visualize">Visualize</button>
248
- <button class="tab-btn" data-tab="playbooks">Playbooks</button>
249
210
  <button class="tab-btn" data-tab="settings">Settings</button>
250
211
  </div>
251
212
 
@@ -304,17 +265,6 @@
304
265
  </div>
305
266
  </div>
306
267
 
307
- <!-- Playbooks Panel -->
308
- <div id="playbooks-panel" class="settings-panel">
309
- <div class="section">
310
- <h3>Playbooks</h3>
311
- <div style="margin-bottom: 10px;">
312
- <input type="text" id="playbook-search-input" placeholder="Search playbooks..." style="width:100%;padding:6px 10px;border-radius:6px;border:1px solid rgba(255,255,255,0.2);background:rgba(255,255,255,0.05);color:#fff;font-size:12px;outline:none;">
313
- </div>
314
- <div id="playbook-list"></div>
315
- </div>
316
- </div>
317
-
318
268
  <!-- Settings Panel -->
319
269
  <div id="settings-panel" class="settings-panel">
320
270
  <div class="section">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-fractal-memory",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
4
4
  "description": "Fractal memory system for OpenCode with semantic search and automatic compression.",
5
5
  "main": "dist/plugin.js",
6
6
  "exports": {