@plumpslabs/kuma 2.3.33 → 2.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/kuma",
3
- "version": "2.3.33",
3
+ "version": "2.4.1",
4
4
  "description": "Safety-first context & orchestration engine for AI coding agents. MCP server with mandatory research pipeline, knowledge graph, impact analysis, decision memory, and safety guard — works with any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -154,12 +154,7 @@ async function getDashboardData() {
154
154
  const stats = {
155
155
  node_count: first(`SELECT COUNT(*) as c FROM nodes`, "c"),
156
156
  edge_count: first(`SELECT COUNT(*) as c FROM edges`, "c"),
157
- gotcha_count: first(`SELECT COUNT(*) as c FROM known_gotchas`, "c"),
158
- feature_count: first(`SELECT COUNT(*) as c FROM nodes WHERE type = 'feature'`, "c"),
159
- health_score: first(
160
- `SELECT COALESCE((SELECT score FROM health_snapshots ORDER BY created_at DESC LIMIT 1), 0) as c`,
161
- "c"
162
- )
157
+ gotcha_count: first(`SELECT COUNT(*) as c FROM known_gotchas`, "c")
163
158
  };
164
159
  const nodes = jsonRows(
165
160
  `SELECT json_group_array(json_object('id',id,'name',name,'type',type,'file_path',file_path,'severity',COALESCE(severity,'medium'),'confidence',COALESCE(confidence,0.8),'metadata',COALESCE(metadata,'{}'))) FROM (SELECT * FROM nodes ORDER BY updated_at DESC)`
@@ -173,9 +168,28 @@ async function getDashboardData() {
173
168
  const features = jsonRows(
174
169
  `SELECT json_group_array(json_object('id',id,'name',name,'metadata',COALESCE(metadata,'{}'))) FROM (SELECT * FROM nodes WHERE type = 'feature' ORDER BY updated_at DESC)`
175
170
  );
176
- const health = jsonRows(
177
- `SELECT json_group_array(json_object('score',score,'summary',REPLACE(REPLACE(COALESCE(summary,''),char(10),' '),char(13),''),'risk_level',risk_level,'created_at',created_at)) FROM (SELECT * FROM health_snapshots ORDER BY created_at DESC LIMIT 10)`
178
- );
171
+ let injectionCount = 0;
172
+ let injectionSavedMs = 0;
173
+ try {
174
+ const root = projectRootFromDb(dbPath);
175
+ const injPath = path.join(root, ".kuma", "injections.jsonl");
176
+ if (fs.existsSync(injPath)) {
177
+ const lines = fs.readFileSync(injPath, "utf-8").split("\n").filter(Boolean);
178
+ const now = Date.now();
179
+ for (const line of lines.slice(-200)) {
180
+ try {
181
+ const entry = JSON.parse(line);
182
+ const age = now - (entry.ts || 0);
183
+ if (age < 24 * 60 * 60 * 1e3) {
184
+ injectionCount++;
185
+ injectionSavedMs += entry.saved_ms || 5e3;
186
+ }
187
+ } catch {
188
+ }
189
+ }
190
+ }
191
+ } catch {
192
+ }
179
193
  const sessionMetrics = readSessionMetrics(dbPath);
180
194
  const verifTotal = first(`SELECT COUNT(*) as c FROM verifications`, "c");
181
195
  const verifPassed = first(`SELECT COUNT(*) as c FROM verifications WHERE passed = 1`, "c");
@@ -205,7 +219,7 @@ async function getDashboardData() {
205
219
  recentSessions
206
220
  };
207
221
  const staleness = detectStaleNodes(db, dbPath);
208
- return { stats, nodes, edges, gotchas, features, health, efficiency, staleness };
222
+ return { stats, nodes, edges, gotchas, efficiency, staleness, injections: { count: injectionCount, savedMs: injectionSavedMs, savedFormatted: Math.round(injectionSavedMs / 6e4) + " min" } };
209
223
  } finally {
210
224
  db.close();
211
225
  }
@@ -250,7 +250,7 @@ header .status .dot.off { background: var(--red); }
250
250
  <div class="tabs">
251
251
  <div class="tab active" data-tab="features" title="Features"><i data-lucide="star" style="width:12px;height:12px"></i><span class="tab-label">Features</span></div>
252
252
  <div class="tab" data-tab="gotchas" title="Gotchas"><i data-lucide="alert-triangle" style="width:12px;height:12px"></i><span class="tab-label">Gotchas</span></div>
253
- <div class="tab" data-tab="health" title="Health"><i data-lucide="heart-pulse" style="width:12px;height:12px"></i><span class="tab-label">Health</span></div>
253
+ <div class="tab" data-tab="injections" title="Shadow Memory"><i data-lucide="zap" style="width:12px;height:12px"></i><span class="tab-label">Injections</span></div>
254
254
  <div class="tab" data-tab="efficiency" title="Efficiency"><i data-lucide="zap" style="width:12px;height:12px"></i><span class="tab-label">Efficiency</span></div>
255
255
  <div class="tab" data-tab="staleness" title="Staleness"><i data-lucide="activity" style="width:12px;height:12px"></i><span class="tab-label">Staleness</span></div>
256
256
  <div class="tab" data-tab="activity" title="Activity"><i data-lucide="bar-chart-3" style="width:12px;height:12px"></i><span class="tab-label">Activity</span></div>
@@ -337,7 +337,7 @@ async function init() {
337
337
  renderGraph(data);
338
338
  renderFeatures(data);
339
339
  renderGotchas(data);
340
- renderHealth(data);
340
+ renderInjections(data);
341
341
  renderEfficiency(data);
342
342
  renderStaleness(data);
343
343
  renderActivity(data);
@@ -355,15 +355,15 @@ function showError(msg) {
355
355
 
356
356
  function renderStats(data) {
357
357
  var s = data.stats || {};
358
- var hc = s.health_score;
359
- var hcColor = hc >= 80 ? "var(--green)" : hc >= 50 ? "var(--yellow)" : "var(--red)";
358
+ var inj = data.injections || {};
359
+ var savedMin = inj.savedFormatted || '0 min';
360
360
  document.getElementById("stats-bar").innerHTML =
361
361
  '<div class="stat"><span class="num">'+(s.node_count||0)+'</span><span class="lbl">nodes</span></div>'+
362
362
  '<div class="stat"><span class="num">'+(s.edge_count||0)+'</span><span class="lbl">edges</span></div>'+
363
- '<div class="stat"><span class="num" style="color:var(--accent-bright)">'+(s.feature_count||0)+'</span><span class="lbl">features</span></div>'+
364
363
  '<div class="stat"><span class="num">'+(s.gotcha_count||0)+'</span><span class="lbl">gotchas</span></div>'+
365
364
  '<div class="stat"><span class="num" style="color:var(--accent)">'+currentDbSize+'</span><span class="lbl">kuma.db</span></div>'+
366
- '<div class="stat"><span class="num" style="color:'+hcColor+'">'+(hc||0)+'</span><span class="lbl">health</span></div>';
365
+ '<div class="stat"><span class="num" style="color:var(--blue)">'+inj.count+'</span><span class="lbl">injections</span></div>'+
366
+ '<div class="stat"><span class="num" style="font-size:13px;color:var(--green)">'+savedMin+'</span><span class="lbl">time saved</span></div>';
367
367
  }
368
368
 
369
369
  function truncateLabel(name, maxLen) {
@@ -791,7 +791,7 @@ async function refetch() {
791
791
  if (status.ok) { dot.className = 'dot on'; txt.textContent = status.project || 'connected'; } else { dot.className = 'dot off'; txt.textContent = 'no kuma.db found'; }
792
792
  var data = await fetch(API+'/dashboard').then(function(r){return r.json()});
793
793
  if (data.error) { showError(data.error); return; }
794
- renderStats(data); renderGraph(data); renderFeatures(data); renderGotchas(data); renderHealth(data); renderEfficiency(data); renderStaleness(data); renderActivity(data);
794
+ renderStats(data); renderGraph(data); renderGotchas(data); renderEfficiency(data); renderStaleness(data); renderInjections(data);
795
795
  lucide.createIcons();
796
796
  } catch(e) { showError('Refetch failed: ' + e.message); }
797
797
  btn.innerHTML = '<i data-lucide="refresh-cw" style="width:13px;height:13px;vertical-align:middle;margin-right:3px"></i> Refetch';
@@ -809,7 +809,7 @@ function setupTabs(){
809
809
  content.innerHTML =
810
810
  '<div class="panel active" id="panel-features"><div class="empty">Loading...</div></div>' +
811
811
  '<div class="panel" id="panel-gotchas"><div class="empty">Loading...</div></div>' +
812
- '<div class="panel" id="panel-health"><div class="empty">Loading...</div></div>' +
812
+ '<div class="panel" id="panel-injections"><div class="empty">Loading...</div></div>' +
813
813
  '<div class="panel" id="panel-efficiency"><div class="empty">Loading...</div></div>' +
814
814
  '<div class="panel" id="panel-staleness"><div class="empty">Loading...</div></div>' +
815
815
  '<div class="panel" id="panel-activity"><div class="empty">Loading...</div></div>';
@@ -864,17 +864,22 @@ function renderGotchas(data){
864
864
  lucide.createIcons();
865
865
  }
866
866
 
867
- function renderHealth(data){
868
- var items = data.health || [];
869
- var el = document.getElementById("panel-health");
867
+ function renderInjections(data) {
868
+ var inj = data.injections || {};
869
+ var el = document.getElementById("panel-injections");
870
870
  if(!el) return;
871
- if(!items.length){el.innerHTML='<div class="empty">No health data yet</div>';return;}
872
- el.innerHTML = items.map(function(h){
873
- var cls = h.score>=80?'good':h.score>=50?'ok':'bad';
874
- var summary = h.summary||h.risk_level||'-';
875
- var date = h.created_at ? new Date(h.created_at*1000).toLocaleDateString() : '-';
876
- return '<div class="health-item"><div class="score '+cls+'">'+h.score+'</div><div class="meta">'+escapeHtml(summary)+' &middot; '+date+'</div></div>';
877
- }).join('');
871
+ if(!inj.count){el.innerHTML='<div class="empty">No shadow memory injections yet. Enable hooks via `kuma init --claude`.</div>';return;}
872
+ el.innerHTML = '<div class="section-title">Shadow Memory (24h)</div>'+
873
+ '<div class="efficiency-grid">'+
874
+ '<div class="eff-item"><span class="eff-num">'+inj.count+'</span><span class="eff-lbl">Injections</span></div>'+
875
+ '<div class="eff-item"><span class="eff-num" style="color:var(--green)">'+inj.savedFormatted+'</span><span class="eff-lbl">Time Saved</span></div>'+
876
+ '<div class="eff-item"><span class="eff-num" style="color:var(--blue)">~'+Math.round(inj.savedMs/5000)+'x</span><span class="eff-lbl">Multiplier</span></div>'+
877
+ '</div>'+
878
+ '<div class="section-title" style="margin-top:16px">Auto-Inject Hooks</div>'+
879
+ '<div class="gotcha-item">Pre-Edit: <code>kuma hook pre-edit</code> — injects gotchas + decisions + history</div>'+
880
+ '<div class="gotcha-item">Pre-Bash: <code>kuma hook pre-bash</code> — injects command-triggered gotchas</div>'+
881
+ '<div class="gotcha-item">Dedupe: files not re-injected within 15 min (I5)</div>'+
882
+ '<div class="gotcha-item">Loop Capture: 4+ edits within 30 min auto-records a gotcha (I3)</div>';
878
883
  }
879
884
 
880
885
  // ============ EFFICIENCY PANEL (GAP 1) ============