@plumpslabs/kuma 2.3.22 → 2.3.24

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.22",
3
+ "version": "2.3.24",
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",
@@ -57,28 +57,67 @@ function getDashboardData() {
57
57
  'node_count', (SELECT COUNT(*) FROM nodes),
58
58
  'edge_count', (SELECT COUNT(*) FROM edges),
59
59
  'gotcha_count', (SELECT COUNT(*) FROM known_gotchas),
60
- 'trajectory_count', (SELECT COUNT(*) FROM trajectories),
61
- 'skill_count', (SELECT COUNT(*) FROM distilled_skills),
60
+ 'feature_count', (SELECT COUNT(*) FROM nodes WHERE type = 'feature'),
62
61
  'health_score', COALESCE((SELECT score FROM health_snapshots ORDER BY created_at DESC LIMIT 1), 0)
63
62
  )
64
63
  `),
65
64
  nodes: queryJson(
66
- `SELECT json_group_array(json_object('id',id,'name',name,'type',type,'file_path',file_path)) FROM nodes ORDER BY updated_at DESC`
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`
67
66
  ),
68
67
  edges: queryJson(
69
- `SELECT json_group_array(json_object('source',source_id,'target',target_id,'relation',type)) FROM edges`
68
+ `SELECT json_group_array(json_object('source',source_id,'target',target_id,'relation',type,'weight',weight)) FROM edges`
70
69
  ),
71
70
  gotchas: queryJson(
72
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`
73
72
  ),
74
- trajectories: queryJson(
75
- `SELECT json_group_array(json_object('id',id,'goal',REPLACE(REPLACE(goal,char(10),' '),char(13),''),'total_duration_ms',total_duration_ms,'success_rate',success_rate,'created_at',created_at)) FROM trajectories ORDER BY created_at DESC LIMIT 20`
73
+ features: queryJson(
74
+ `SELECT json_group_array(json_object('id',id,'name',name,'metadata',COALESCE(metadata,'{}'))) FROM nodes WHERE type = 'feature' ORDER BY name`
76
75
  ),
77
76
  health: queryJson(
78
77
  `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 health_snapshots ORDER BY created_at DESC LIMIT 10`
79
78
  )
80
79
  };
81
80
  }
81
+ function getNodeDetail(nodeId) {
82
+ const escaped = nodeId.replace(/'/g, "''");
83
+ const shortName = nodeId.split("::").pop()?.replace(/'/g, "''") || "";
84
+ 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}'`
86
+ );
87
+ const nodeObj = Array.isArray(node) ? node[0] : node;
88
+ if (!nodeObj) return null;
89
+ const rawOut = query(
90
+ `SELECT json_object('target',e.target_id,'relation',e.type,'weight',e.weight,'target_name',n.name,'target_type',n.type) FROM edges e LEFT JOIN nodes n ON n.id = e.target_id WHERE e.source_id = '${escaped}'`
91
+ );
92
+ const outgoing = rawOut ? rawOut.split("\n").filter(Boolean).map((r) => {
93
+ try {
94
+ return JSON.parse(r);
95
+ } catch {
96
+ return null;
97
+ }
98
+ }).filter(Boolean) : [];
99
+ const rawIn = query(
100
+ `SELECT json_object('source',e.source_id,'relation',e.type,'weight',e.weight,'source_name',n.name,'source_type',n.type) FROM edges e LEFT JOIN nodes n ON n.id = e.source_id WHERE e.target_id = '${escaped}'`
101
+ );
102
+ const incoming = rawIn ? rawIn.split("\n").filter(Boolean).map((r) => {
103
+ try {
104
+ return JSON.parse(r);
105
+ } catch {
106
+ return null;
107
+ }
108
+ }).filter(Boolean) : [];
109
+ 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}%'`
111
+ );
112
+ const gotchas = rawGotcha ? rawGotcha.split("\n").filter(Boolean).map((r) => {
113
+ try {
114
+ return JSON.parse(r);
115
+ } catch {
116
+ return null;
117
+ }
118
+ }).filter(Boolean) : [];
119
+ return { node: nodeObj, outgoing, incoming, gotchas };
120
+ }
82
121
 
83
122
  // src/api.ts
84
123
  import fs from "fs";
@@ -106,6 +145,16 @@ api.get("/dashboard", (c) => {
106
145
  return c.json({ error: e.message }, 500);
107
146
  }
108
147
  });
148
+ api.get("/node/:id", (c) => {
149
+ try {
150
+ const nodeId = c.req.param("id");
151
+ const detail = getNodeDetail(nodeId);
152
+ if (!detail) return c.json({ error: "Node not found" }, 404);
153
+ return c.json(detail);
154
+ } catch (e) {
155
+ return c.json({ error: e.message }, 500);
156
+ }
157
+ });
109
158
  api.post("/reset-db", (c) => {
110
159
  try {
111
160
  const dbPath = findKumaDb();