@plumpslabs/kuma 2.3.30 → 2.3.31

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.30",
3
+ "version": "2.3.31",
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",
@@ -62,13 +62,13 @@ function getDashboardData() {
62
62
  )
63
63
  `),
64
64
  nodes: queryJson(
65
- `SELECT json_group_array(json_object('id',id,'name',name,'type',type,'file_path',file_path,'metadata',COALESCE(metadata,'{}'))) FROM nodes ORDER BY updated_at DESC`
65
+ `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 nodes ORDER BY updated_at DESC`
66
66
  ),
67
67
  edges: queryJson(
68
68
  `SELECT json_group_array(json_object('source',source_id,'target',target_id,'relation',type,'weight',weight)) FROM edges`
69
69
  ),
70
70
  gotchas: queryJson(
71
- `SELECT json_group_array(json_object('id',id,'file_path',file_path,'description',REPLACE(REPLACE(description,char(10),' '),char(13),''),'severity',severity,'workaround',REPLACE(REPLACE(COALESCE(workaround,''),char(10),' '),char(13),''))) FROM known_gotchas ORDER BY severity DESC`
71
+ `SELECT json_group_array(json_object('id',id,'file_path',file_path,'description',REPLACE(REPLACE(description,char(10),' '),char(13),''),'severity',severity,'workaround',REPLACE(REPLACE(COALESCE(workaround,''),char(10),' '),char(13),''),'added_by',COALESCE(added_by,'agent'),'created_at',created_at)) FROM known_gotchas ORDER BY severity DESC`
72
72
  ),
73
73
  features: queryJson(
74
74
  `SELECT json_group_array(json_object('id',id,'name',name,'metadata',COALESCE(metadata,'{}'))) FROM nodes WHERE type = 'feature' ORDER BY name`
@@ -80,9 +80,12 @@ function getDashboardData() {
80
80
  }
81
81
  function getNodeDetail(nodeId) {
82
82
  const escaped = nodeId.replace(/'/g, "''");
83
- const shortName = nodeId.split("::").pop()?.replace(/'/g, "''") || "";
83
+ const parts = nodeId.split("::");
84
+ const shortName = parts.pop()?.replace(/'/g, "''") || "";
85
+ const filePath = parts[0] === "gotcha" && parts.length >= 2 ? parts[1]?.replace(/'/g, "''") : "";
86
+ const scope = parts[0] === "research" && parts.length >= 2 ? parts.slice(1).join("::").replace(/'/g, "''") : "";
84
87
  const node = queryJson(
85
- `SELECT json_object('id',id,'name',name,'type',type,'file_path',file_path,'metadata',COALESCE(metadata,'{}'),'updated_at',updated_at) FROM nodes WHERE id = '${escaped}'`
88
+ `SELECT json_object('id',id,'name',name,'type',type,'file_path',COALESCE(file_path,''),'metadata',COALESCE(metadata,'{}'),'severity',COALESCE(severity,'medium'),'confidence',COALESCE(confidence,0.8),'last_verified_at',last_verified_at,'created_at',created_at,'updated_at',updated_at) FROM nodes WHERE id = '${escaped}'`
86
89
  );
87
90
  const nodeObj = Array.isArray(node) ? node[0] : node;
88
91
  if (!nodeObj) return null;
@@ -106,8 +109,9 @@ function getNodeDetail(nodeId) {
106
109
  return null;
107
110
  }
108
111
  }).filter(Boolean) : [];
112
+ const gotchaFilter = filePath ? `file_path = '${filePath}'` : `file_path = '${escaped}' OR file_path LIKE '%${shortName}%'`;
109
113
  const rawGotcha = query(
110
- `SELECT json_object('id',id,'description',REPLACE(REPLACE(description,char(10),' '),char(13),''),'severity',severity) FROM known_gotchas WHERE file_path = '${escaped}' OR file_path LIKE '%${shortName}%'`
114
+ `SELECT json_object('id',id,'file_path',file_path,'description',REPLACE(REPLACE(description,char(10),' '),char(13),''),'severity',severity,'workaround',REPLACE(REPLACE(COALESCE(workaround,''),char(10),' '),char(13),''),'added_by',COALESCE(added_by,'agent'),'created_at',created_at) FROM known_gotchas WHERE ${gotchaFilter}`
111
115
  );
112
116
  const gotchas = rawGotcha ? rawGotcha.split("\n").filter(Boolean).map((r) => {
113
117
  try {
@@ -658,13 +658,49 @@ async function openNodeModal(nodeId) {
658
658
  html += '<div class="modal-section">';
659
659
  html += '<h3>Info</h3>';
660
660
  html += '<div class="modal-kv"><span class="key">Type</span><span class="val"><span style="color:var(--accent)">'+escapeHtml(n.type)+'</span></span></div>';
661
+ html += '<div class="modal-kv"><span class="key">ID</span><span class="val" style="font-family:monospace;font-size:10px;color:var(--muted)">'+escapeHtml(n.id)+'</span></div>';
661
662
  if (n.file_path) html += '<div class="modal-kv"><span class="key">Path</span><span class="val" style="color:var(--accent2)">'+escapeHtml(n.file_path)+'</span></div>';
662
- if (meta.description) html += '<div class="modal-kv"><span class="key">Description</span><span class="val">'+escapeHtml(meta.description)+'</span></div>';
663
- if (meta.tags && meta.tags.length) html += '<div class="modal-kv"><span class="key">Tags</span><span class="val">'+meta.tags.map(function(t){return '<span class="tag">'+escapeHtml(t)+'</span>';}).join(' ')+'</span></div>';
664
- if (meta.risk) html += '<div class="modal-kv"><span class="key">Risk</span><span class="val" style="color:'+(meta.risk==='critical'?'var(--red)':meta.risk==='high'?'var(--orange)':meta.risk==='medium'?'var(--yellow)':'var(--green)')+'">'+escapeHtml(meta.risk)+'</span></div>';
663
+ if (n.severity) html += '<div class="modal-kv"><span class="key">Severity</span><span class="val" style="color:'+(n.severity==='critical'?'var(--red)':n.severity==='high'?'var(--orange)':n.severity==='medium'?'var(--yellow)':'var(--green)')+'">'+escapeHtml(n.severity)+'</span></div>';
664
+ if (n.confidence !== undefined && n.confidence !== null) html += '<div class="modal-kv"><span class="key">Confidence</span><span class="val" style="color:var(--accent)">'+escapeHtml(String(n.confidence))+'</span></div>';
665
+ if (n.last_verified_at) html += '<div class="modal-kv"><span class="key">Last Verified</span><span class="val">'+new Date(n.last_verified_at*1000).toLocaleString()+'</span></div>';
666
+ if (n.created_at) html += '<div class="modal-kv"><span class="key">Created</span><span class="val">'+new Date(n.created_at*1000).toLocaleString()+'</span></div>';
665
667
  if (n.updated_at) html += '<div class="modal-kv"><span class="key">Updated</span><span class="val">'+new Date(n.updated_at*1000).toLocaleString()+'</span></div>';
666
668
  html += '</div>';
667
669
 
670
+ // ALL metadata fields — render every key dynamically
671
+ var metaKeys = Object.keys(meta);
672
+ if (metaKeys.length > 0) {
673
+ html += '<div class="modal-section">';
674
+ html += '<h3>Metadata ('+metaKeys.length+' fields)</h3>';
675
+ metaKeys.forEach(function(key) {
676
+ var val = meta[key];
677
+ if (val === null || val === undefined || val === '') return;
678
+ var display = '';
679
+ var color = 'var(--text)';
680
+ if (Array.isArray(val)) {
681
+ if (val.length === 0) return;
682
+ display = val.join(' → ');
683
+ color = 'var(--accent2)';
684
+ } else if (typeof val === 'object') {
685
+ display = JSON.stringify(val, null, 2);
686
+ color = 'var(--muted)';
687
+ } else if (typeof val === 'number') {
688
+ display = String(val);
689
+ color = 'var(--accent)';
690
+ } else if (key === 'severity' || key === 'risk') {
691
+ display = String(val);
692
+ color = val === 'critical' ? 'var(--red)' : val === 'high' ? 'var(--orange)' : val === 'medium' ? 'var(--yellow)' : 'var(--green)';
693
+ } else if (key === 'stale') {
694
+ display = val === 1 ? 'YES — needs heal' : 'No';
695
+ color = val === 1 ? 'var(--red)' : 'var(--green)';
696
+ } else {
697
+ display = String(val);
698
+ }
699
+ html += '<div class="modal-kv"><span class="key">'+escapeHtml(key)+'</span><span class="val" style="color:'+color+'">'+escapeHtml(display)+'</span></div>';
700
+ });
701
+ html += '</div>';
702
+ }
703
+
668
704
  // Outgoing relations
669
705
  if (outgoing.length) {
670
706
  html += '<div class="modal-section"><h3>Outgoing ('+outgoing.length+')</h3>';
@@ -704,8 +740,12 @@ async function openNodeModal(nodeId) {
704
740
  var got = typeof g === 'string' ? JSON.parse(g) : g;
705
741
  html += '<div class="gotcha-item" style="border-color:var(--'+(got.severity==='critical'?'red':got.severity==='high'?'orange':got.severity==='medium'?'yellow':'green')+')">';
706
742
  html += '<div class="desc">'+escapeHtml(got.description)+'</div>';
707
- html += '<span class="sev-tag sev-'+got.severity+'">'+got.severity+'</span>';
708
- html += '</div>';
743
+ html += '<div style="display:flex;gap:8px;align-items:center;margin-top:4px">';
744
+ html += '<span class="sev-tag sev-'+got.severity+'">'+escapeHtml(got.severity)+'</span>';
745
+ if (got.workaround) html += '<span style="color:var(--accent2);font-size:10px">Workaround: '+escapeHtml(got.workaround)+'</span>';
746
+ if (got.added_by) html += '<span style="color:var(--muted);font-size:10px">by: '+escapeHtml(got.added_by)+'</span>';
747
+ if (got.created_at) html += '<span style="color:var(--muted);font-size:10px">'+new Date(got.created_at*1000).toLocaleDateString()+'</span>';
748
+ html += '</div></div>';
709
749
  });
710
750
  html += '</div>';
711
751
  }