@plumpslabs/kuma 2.3.6 → 2.3.8
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 +25 -3
- package/dist/{chunk-K64NSHBR.js → chunk-4OB3XMHT.js} +2 -2
- package/dist/chunk-53J56QCQ.js +282 -0
- package/dist/{chunk-FOQQ2CSL.js → chunk-FL2TLLMX.js} +1 -1
- package/dist/{chunk-GDNAWLHF.js → chunk-NAM7SCBT.js} +63 -1
- package/dist/chunk-PTEPJK6M.js +155 -0
- package/dist/{chunk-X5TPBDKO.js → chunk-RTGLQDMI.js} +2 -2
- package/dist/{chunk-BI7KD3SG.js → chunk-SLRRDKQ2.js} +2 -2
- package/dist/contextDigest-4PCK3DSM.js +177 -0
- package/dist/domainRules-LDZPOIGZ.js +20 -0
- package/dist/index.js +261 -182
- package/dist/kumaAstValidator-CNM7FHYA.js +150 -0
- package/dist/{kumaDb-DJUDLYBJ.js → kumaDb-6D53ERFP.js} +1 -1
- package/dist/kumaDriftDetector-OESI5GTC.js +237 -0
- package/dist/kumaGotchas-DU5H6N7X.js +151 -0
- package/dist/{kumaGraph-35TAIBWD.js → kumaGraph-AWP743TJ.js} +2 -2
- package/dist/{kumaMemory-5SR3TGRL.js → kumaMemory-7TCUDVZX.js} +2 -2
- package/dist/{kumaMiner-BIDSZE3Q.js → kumaMiner-FFXSRHAO.js} +5 -5
- package/dist/kumaPolicyEngine-S2PXN3C7.js +311 -0
- package/dist/kumaSearch-QGB4QVXS.js +321 -0
- package/dist/{kumaVerifier-B4D7NOFT.js → kumaVerifier-ARSGPZAM.js} +27 -4
- package/dist/kumaVisualize-TECABHUY.js +248 -0
- package/dist/safetyAudit-55IVCG5B.js +12 -0
- package/dist/{safetyScore-PJGRRBWP.js → safetyScore-GR5N55CU.js} +5 -5
- package/dist/{sessionMemory-HBBXUSIP.js → sessionMemory-CA34TCPF.js} +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDb
|
|
3
|
+
} from "./chunk-NAM7SCBT.js";
|
|
4
|
+
import "./chunk-E2KFPEBT.js";
|
|
5
|
+
|
|
6
|
+
// src/engine/kumaVisualize.ts
|
|
7
|
+
async function visualizeGraph(options = {}) {
|
|
8
|
+
const {
|
|
9
|
+
type = "flowchart",
|
|
10
|
+
scope,
|
|
11
|
+
maxNodes = 30,
|
|
12
|
+
// depth unused: kept for API consistency
|
|
13
|
+
format = "mermaid"
|
|
14
|
+
} = options;
|
|
15
|
+
try {
|
|
16
|
+
const db = await getDb();
|
|
17
|
+
let nodeSql = `SELECT id, type, name, file_path FROM nodes WHERE 1=1`;
|
|
18
|
+
const nodeBind = [];
|
|
19
|
+
if (scope) {
|
|
20
|
+
nodeSql += ` AND (name LIKE ? OR file_path LIKE ? OR id LIKE ?)`;
|
|
21
|
+
nodeBind.push(`%${scope}%`, `%${scope}%`, `%${scope}%`);
|
|
22
|
+
}
|
|
23
|
+
nodeSql += ` ORDER BY updated_at DESC LIMIT ?`;
|
|
24
|
+
nodeBind.push(maxNodes);
|
|
25
|
+
const nodeStmt = db.prepare(nodeSql);
|
|
26
|
+
nodeStmt.bind(nodeBind);
|
|
27
|
+
const nodes = [];
|
|
28
|
+
while (nodeStmt.step()) {
|
|
29
|
+
nodes.push(nodeStmt.getAsObject());
|
|
30
|
+
}
|
|
31
|
+
nodeStmt.free();
|
|
32
|
+
if (nodes.length === 0) {
|
|
33
|
+
return format === "mermaid" ? "```mermaid\nflowchart TD\n Start[Empty Graph \u2014 No nodes found]\n```" : "\u{1F4ED} **Empty Graph** \u2014 No nodes found to visualize.";
|
|
34
|
+
}
|
|
35
|
+
const nodeIds = nodes.map((n) => n.id);
|
|
36
|
+
const edgeSql = `
|
|
37
|
+
SELECT e.source_id, e.target_id, e.type, e.weight,
|
|
38
|
+
sn.name AS source_name, tn.name AS target_name
|
|
39
|
+
FROM edges e
|
|
40
|
+
JOIN nodes sn ON sn.id = e.source_id
|
|
41
|
+
JOIN nodes tn ON tn.id = e.target_id
|
|
42
|
+
WHERE (e.source_id IN (${nodeIds.map(() => "?").join(",")})
|
|
43
|
+
OR e.target_id IN (${nodeIds.map(() => "?").join(",")}))
|
|
44
|
+
AND e.source_id != e.target_id
|
|
45
|
+
ORDER BY e.weight DESC
|
|
46
|
+
LIMIT 100
|
|
47
|
+
`;
|
|
48
|
+
const edgeBind = [...nodeIds, ...nodeIds];
|
|
49
|
+
const edgeStmt = db.prepare(edgeSql);
|
|
50
|
+
edgeStmt.bind(edgeBind);
|
|
51
|
+
const edges = [];
|
|
52
|
+
while (edgeStmt.step()) {
|
|
53
|
+
edges.push(edgeStmt.getAsObject());
|
|
54
|
+
}
|
|
55
|
+
edgeStmt.free();
|
|
56
|
+
switch (type) {
|
|
57
|
+
case "flowchart":
|
|
58
|
+
return formatFlowchart(nodes, edges, format);
|
|
59
|
+
case "dependency":
|
|
60
|
+
return formatDependencyGraph(nodes, edges, format);
|
|
61
|
+
case "mindmap":
|
|
62
|
+
return formatMindMap(nodes, edges, format);
|
|
63
|
+
default:
|
|
64
|
+
return formatFlowchart(nodes, edges, format);
|
|
65
|
+
}
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return `Error generating visualization: ${err}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function formatFlowchart(nodes, edges, _format) {
|
|
71
|
+
void _format;
|
|
72
|
+
const lines = ["```mermaid", "flowchart TD"];
|
|
73
|
+
for (const node of nodes) {
|
|
74
|
+
const id = sanitizeId(node.id);
|
|
75
|
+
const name = truncate(node.name, 30);
|
|
76
|
+
const nodeType = node.type;
|
|
77
|
+
const shape = getNodeShape(nodeType);
|
|
78
|
+
if (shape) {
|
|
79
|
+
lines.push(` ${id}${shape}${name}${reverseShape(shape)}`);
|
|
80
|
+
} else {
|
|
81
|
+
lines.push(` ${id}[${name}]`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
for (const edge of edges) {
|
|
85
|
+
const sourceId = sanitizeId(edge.source_id);
|
|
86
|
+
const targetId = sanitizeId(edge.target_id);
|
|
87
|
+
const edgeType = edge.type;
|
|
88
|
+
void edge.weight;
|
|
89
|
+
const label = truncate(edgeType, 15);
|
|
90
|
+
const edgeStyle = getEdgeStyle(edgeType);
|
|
91
|
+
lines.push(` ${sourceId}${edgeStyle}|${label}|${targetId}`);
|
|
92
|
+
}
|
|
93
|
+
lines.push("```");
|
|
94
|
+
return lines.join("\n");
|
|
95
|
+
}
|
|
96
|
+
function formatDependencyGraph(nodes, edges, _format) {
|
|
97
|
+
void _format;
|
|
98
|
+
const lines = ["```mermaid", "flowchart LR"];
|
|
99
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const node of nodes) {
|
|
101
|
+
const type = node.type || "unknown";
|
|
102
|
+
if (!grouped.has(type)) grouped.set(type, []);
|
|
103
|
+
grouped.get(type).push(node);
|
|
104
|
+
}
|
|
105
|
+
let subgraphIndex = 0;
|
|
106
|
+
for (const [type, typeNodes] of grouped) {
|
|
107
|
+
if (typeNodes.length < 1) continue;
|
|
108
|
+
lines.push(` subgraph ${type.toUpperCase()}[${type.toUpperCase()}]`);
|
|
109
|
+
for (const node of typeNodes) {
|
|
110
|
+
const id = sanitizeId(node.id);
|
|
111
|
+
const name = truncate(node.name, 25);
|
|
112
|
+
lines.push(` ${id}(${name})`);
|
|
113
|
+
}
|
|
114
|
+
lines.push(" end");
|
|
115
|
+
subgraphIndex++;
|
|
116
|
+
}
|
|
117
|
+
for (const edge of edges) {
|
|
118
|
+
const sourceId = sanitizeId(edge.source_id);
|
|
119
|
+
const targetId = sanitizeId(edge.target_id);
|
|
120
|
+
const edgeType = edge.type;
|
|
121
|
+
const style = getEdgeStyle(edgeType);
|
|
122
|
+
lines.push(` ${sourceId}${style}|${edgeType}|${targetId}`);
|
|
123
|
+
}
|
|
124
|
+
lines.push("```");
|
|
125
|
+
return lines.join("\n");
|
|
126
|
+
}
|
|
127
|
+
function formatMindMap(nodes, edges, _format) {
|
|
128
|
+
void _format;
|
|
129
|
+
const lines = ["```mermaid", "mindmap"];
|
|
130
|
+
const hasIncoming = /* @__PURE__ */ new Set();
|
|
131
|
+
for (const edge of edges) {
|
|
132
|
+
hasIncoming.add(edge.target_id);
|
|
133
|
+
}
|
|
134
|
+
const roots = nodes.filter((n) => !hasIncoming.has(n.id));
|
|
135
|
+
if (roots.length === 0 && nodes.length > 0) {
|
|
136
|
+
const root = nodes[0];
|
|
137
|
+
lines.push(` root((${truncate(root.name, 25)}))`);
|
|
138
|
+
for (const node of nodes.slice(1)) {
|
|
139
|
+
lines.push(` ${sanitizeId(node.id)}(${truncate(node.name, 25)})`);
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
for (const root of roots.slice(0, 3)) {
|
|
143
|
+
lines.push(` root((${truncate(root.name, 25)}))`);
|
|
144
|
+
}
|
|
145
|
+
const others = nodes.filter((n) => !roots.includes(n));
|
|
146
|
+
for (const node of others.slice(0, 10)) {
|
|
147
|
+
const parent = edges.find((e) => e.source_id === node.id || e.target_id === node.id);
|
|
148
|
+
const indent = parent ? " " : " ";
|
|
149
|
+
lines.push(`${indent}${sanitizeId(node.id)}(${truncate(node.name, 25)})`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
lines.push("```");
|
|
153
|
+
return lines.join("\n");
|
|
154
|
+
}
|
|
155
|
+
function sanitizeId(id) {
|
|
156
|
+
const safe = id.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "n$1").substring(0, 40);
|
|
157
|
+
return safe || "n0";
|
|
158
|
+
}
|
|
159
|
+
function truncate(str, maxLen) {
|
|
160
|
+
if (str.length <= maxLen) return str;
|
|
161
|
+
return str.substring(0, maxLen - 3) + "...";
|
|
162
|
+
}
|
|
163
|
+
function getNodeShape(type) {
|
|
164
|
+
switch (type) {
|
|
165
|
+
case "function":
|
|
166
|
+
return "([";
|
|
167
|
+
case "file":
|
|
168
|
+
return "[";
|
|
169
|
+
case "test":
|
|
170
|
+
return "{";
|
|
171
|
+
case "api_route":
|
|
172
|
+
return "[[";
|
|
173
|
+
case "db_table":
|
|
174
|
+
return ">";
|
|
175
|
+
// database shape approximation
|
|
176
|
+
case "class":
|
|
177
|
+
return "[";
|
|
178
|
+
case "interface":
|
|
179
|
+
return "(";
|
|
180
|
+
case "module":
|
|
181
|
+
return "[";
|
|
182
|
+
case "variable":
|
|
183
|
+
return "(";
|
|
184
|
+
default:
|
|
185
|
+
return "[";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function reverseShape(shape) {
|
|
189
|
+
const map = {
|
|
190
|
+
"([": "])",
|
|
191
|
+
"[": "]",
|
|
192
|
+
"{": "}",
|
|
193
|
+
"[[": "]]",
|
|
194
|
+
">": "<]",
|
|
195
|
+
"(": ")"
|
|
196
|
+
};
|
|
197
|
+
return map[shape] || "]";
|
|
198
|
+
}
|
|
199
|
+
function getEdgeStyle(type) {
|
|
200
|
+
switch (type) {
|
|
201
|
+
case "calls":
|
|
202
|
+
return " ==> ";
|
|
203
|
+
case "imports":
|
|
204
|
+
return " -.-> ";
|
|
205
|
+
case "defines":
|
|
206
|
+
return " --- ";
|
|
207
|
+
case "tests":
|
|
208
|
+
return " -.-> ";
|
|
209
|
+
case "routes":
|
|
210
|
+
return " ==> ";
|
|
211
|
+
case "implements":
|
|
212
|
+
return " -.-> ";
|
|
213
|
+
case "extends":
|
|
214
|
+
return " ==> ";
|
|
215
|
+
case "depends_on":
|
|
216
|
+
return " -.-> ";
|
|
217
|
+
case "owns":
|
|
218
|
+
return " --- ";
|
|
219
|
+
case "modified_by":
|
|
220
|
+
return " -.-> ";
|
|
221
|
+
default:
|
|
222
|
+
return " --- ";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function generateVisualizeReport(options = {}) {
|
|
226
|
+
const diagram = await visualizeGraph(options);
|
|
227
|
+
const lines = [
|
|
228
|
+
"\u{1F3A8} **Knowledge Graph Visualization**",
|
|
229
|
+
"\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501",
|
|
230
|
+
"",
|
|
231
|
+
diagram,
|
|
232
|
+
"",
|
|
233
|
+
"\u{1F4CB} **Rendering Instructions:**",
|
|
234
|
+
"- Paste the Mermaid code above into any Mermaid-compatible viewer",
|
|
235
|
+
"- GitHub: Mermaid code blocks render automatically",
|
|
236
|
+
"- VS Code: Install 'Markdown Preview Mermaid Support' extension",
|
|
237
|
+
"- Online: https://mermaid.live",
|
|
238
|
+
"",
|
|
239
|
+
"\u{1F4A1} Use `kuma_visualize({ type: 'dependency' })` for clustered view",
|
|
240
|
+
"\u{1F4A1} Use `kuma_visualize({ type: 'mindmap' })` for overview",
|
|
241
|
+
"\u{1F4A1} Use `kuma_visualize({ scope: 'auth' })` to filter by topic"
|
|
242
|
+
];
|
|
243
|
+
return lines.join("\n");
|
|
244
|
+
}
|
|
245
|
+
export {
|
|
246
|
+
generateVisualizeReport,
|
|
247
|
+
visualizeGraph
|
|
248
|
+
};
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
getGitDiffStat,
|
|
3
3
|
getSessionStats,
|
|
4
4
|
getUnresolvedCount
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-FL2TLLMX.js";
|
|
6
6
|
import {
|
|
7
7
|
sessionMemory
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-SLRRDKQ2.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
|
|
11
11
|
// src/engine/safetyScore.ts
|
|
@@ -60,7 +60,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
60
60
|
totalScore += 20;
|
|
61
61
|
}
|
|
62
62
|
try {
|
|
63
|
-
const { getDb } = await import("./kumaDb-
|
|
63
|
+
const { getDb } = await import("./kumaDb-6D53ERFP.js");
|
|
64
64
|
const db = await getDb();
|
|
65
65
|
const nodeCount = db.exec("SELECT COUNT(*) as c FROM nodes")[0]?.values[0][0] ?? 0;
|
|
66
66
|
const edgeCount = db.exec("SELECT COUNT(*) as c FROM edges")[0]?.values[0][0] ?? 0;
|
|
@@ -91,7 +91,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
91
91
|
totalScore += 5;
|
|
92
92
|
}
|
|
93
93
|
try {
|
|
94
|
-
const { getDb } = await import("./kumaDb-
|
|
94
|
+
const { getDb } = await import("./kumaDb-6D53ERFP.js");
|
|
95
95
|
const db = await getDb();
|
|
96
96
|
const researchCount = db.exec("SELECT COUNT(*) as c FROM research_cache")[0]?.values[0][0] ?? 0;
|
|
97
97
|
checks.push({
|
|
@@ -116,7 +116,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
116
116
|
const hasRunTests = stats.hasRunTests;
|
|
117
117
|
let latestVerif = null;
|
|
118
118
|
try {
|
|
119
|
-
const { getLatestVerifications } = await import("./kumaDb-
|
|
119
|
+
const { getLatestVerifications } = await import("./kumaDb-6D53ERFP.js");
|
|
120
120
|
const verifs = await getLatestVerifications(1);
|
|
121
121
|
if (verifs.length > 0) latestVerif = verifs[0];
|
|
122
122
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.8",
|
|
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",
|