@plumpslabs/kuma 2.3.5 → 2.3.7
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/dist/{chunk-PH4JKL6T.js → chunk-BI7KD3SG.js} +15 -7
- package/dist/{chunk-ZIFLGJ5R.js → chunk-FOQQ2CSL.js} +1 -1
- package/dist/{chunk-2NBESJTH.js → chunk-GDNAWLHF.js} +20 -9
- package/dist/{chunk-LLAPHR5W.js → chunk-K64NSHBR.js} +8 -7
- package/dist/{chunk-6QX4LBHV.js → chunk-X5TPBDKO.js} +2 -2
- package/dist/index.js +157 -28
- package/dist/{kumaDb-FQ3YKJ6K.js → kumaDb-DJUDLYBJ.js} +1 -1
- package/dist/{kumaGraph-ABVEVF4M.js → kumaGraph-35TAIBWD.js} +2 -2
- package/dist/{kumaMemory-DDZQXMFZ.js → kumaMemory-5SR3TGRL.js} +2 -2
- package/dist/{kumaMiner-GP6BITKV.js → kumaMiner-P5KCUSF7.js} +42 -8
- package/dist/kumaSearch-LG2OYEJY.js +321 -0
- package/dist/{kumaVerifier-7TFWLEPG.js → kumaVerifier-LFRNIGSL.js} +4 -4
- package/dist/kumaVisualize-ODCWFSUY.js +248 -0
- package/dist/{safetyScore-VYT6OMVG.js → safetyScore-PJGRRBWP.js} +14 -6
- package/dist/{sessionMemory-PGYVS3BK.js → sessionMemory-HBBXUSIP.js} +1 -1
- package/package.json +1 -1
|
@@ -320,7 +320,7 @@ No unresolved issues.`;
|
|
|
320
320
|
*/
|
|
321
321
|
async autoTrackToDb(toolName, params, timestamp) {
|
|
322
322
|
try {
|
|
323
|
-
const { getDb, saveDb } = await import("./kumaDb-
|
|
323
|
+
const { getDb, saveDb } = await import("./kumaDb-DJUDLYBJ.js");
|
|
324
324
|
const db = await getDb();
|
|
325
325
|
db.run(
|
|
326
326
|
`INSERT INTO tool_calls (tool_name, params, success, duration_ms, created_at) VALUES (?, ?, 1, 0, ?)`,
|
|
@@ -466,14 +466,14 @@ No unresolved issues.`;
|
|
|
466
466
|
const memoriesDir = this.memoriesDir();
|
|
467
467
|
if (fs.existsSync(memoriesDir)) {
|
|
468
468
|
try {
|
|
469
|
-
const files = fs.readdirSync(memoriesDir);
|
|
469
|
+
const files = fs.readdirSync(memoriesDir).filter((f) => f.endsWith(".md")).slice(0, 3);
|
|
470
470
|
for (const file of files) {
|
|
471
|
-
if (!file.endsWith(".md")) continue;
|
|
472
471
|
const filePath = path.join(memoriesDir, file);
|
|
473
472
|
try {
|
|
474
473
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
475
474
|
const lines = content.split("\n");
|
|
476
|
-
|
|
475
|
+
const maxLinesPerFile = 500;
|
|
476
|
+
for (let i = 0; i < Math.min(lines.length, maxLinesPerFile); i++) {
|
|
477
477
|
if (lines[i].toLowerCase().includes(q)) {
|
|
478
478
|
const topicName = file.replace(/\.md$/, "");
|
|
479
479
|
results.push({
|
|
@@ -571,10 +571,18 @@ No unresolved issues.`;
|
|
|
571
571
|
if (recentCalls.length < 6) return { isLooping: false };
|
|
572
572
|
const toolCounts = /* @__PURE__ */ new Map();
|
|
573
573
|
for (const call of recentCalls) {
|
|
574
|
-
|
|
574
|
+
const existing = toolCounts.get(call.toolName);
|
|
575
|
+
const action = call.params.action || "";
|
|
576
|
+
if (existing) {
|
|
577
|
+
existing.count++;
|
|
578
|
+
existing.actions.add(action);
|
|
579
|
+
} else {
|
|
580
|
+
toolCounts.set(call.toolName, { count: 1, actions: /* @__PURE__ */ new Set([action]) });
|
|
581
|
+
}
|
|
575
582
|
}
|
|
576
|
-
for (const [toolName, count] of toolCounts) {
|
|
583
|
+
for (const [toolName, { count, actions }] of toolCounts) {
|
|
577
584
|
if (count >= 4) {
|
|
585
|
+
if (actions.size >= 3) continue;
|
|
578
586
|
return {
|
|
579
587
|
isLooping: true,
|
|
580
588
|
toolName,
|
|
@@ -597,7 +605,7 @@ No unresolved issues.`;
|
|
|
597
605
|
this.addModifiedFile(filePath);
|
|
598
606
|
}
|
|
599
607
|
try {
|
|
600
|
-
const { recordChange } = await import("./kumaDb-
|
|
608
|
+
const { recordChange } = await import("./kumaDb-DJUDLYBJ.js");
|
|
601
609
|
const gitHash = this.getGitHead();
|
|
602
610
|
await recordChange({ filePath, changeType, symbol, gitCommitHash: gitHash || void 0 });
|
|
603
611
|
} catch {
|
|
@@ -36,18 +36,29 @@ async function initDb() {
|
|
|
36
36
|
dbInstance = db;
|
|
37
37
|
return db;
|
|
38
38
|
}
|
|
39
|
+
var _saveTimeout = null;
|
|
40
|
+
var _pendingDb = null;
|
|
41
|
+
var SAVE_DEBOUNCE_MS = 100;
|
|
39
42
|
function saveDb(db) {
|
|
40
43
|
const d = db ?? dbInstance;
|
|
41
44
|
if (!d) return;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
_pendingDb = d;
|
|
46
|
+
if (_saveTimeout) return;
|
|
47
|
+
_saveTimeout = setTimeout(() => {
|
|
48
|
+
_saveTimeout = null;
|
|
49
|
+
const targetDb = _pendingDb;
|
|
50
|
+
_pendingDb = null;
|
|
51
|
+
if (!targetDb) return;
|
|
52
|
+
try {
|
|
53
|
+
const kumaDir = getKumaDir();
|
|
54
|
+
const dbPath = path.join(kumaDir, DB_FILENAME);
|
|
55
|
+
const data = targetDb.export();
|
|
56
|
+
const buffer = Buffer.from(data);
|
|
57
|
+
fs.writeFileSync(dbPath, buffer);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(`[KumaDB] Failed to save database: ${err}`);
|
|
60
|
+
}
|
|
61
|
+
}, SAVE_DEBOUNCE_MS);
|
|
51
62
|
}
|
|
52
63
|
function createSchema(db) {
|
|
53
64
|
db.run(`CREATE TABLE IF NOT EXISTS nodes (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDb,
|
|
3
3
|
saveDb
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GDNAWLHF.js";
|
|
5
5
|
import {
|
|
6
6
|
getProjectRoot
|
|
7
7
|
} from "./chunk-E2KFPEBT.js";
|
|
@@ -458,7 +458,7 @@ The two nodes may not be connected in the knowledge graph yet. Use more tools to
|
|
|
458
458
|
}
|
|
459
459
|
async function buildFromSessionMemory() {
|
|
460
460
|
try {
|
|
461
|
-
const { sessionMemory } = await import("./sessionMemory-
|
|
461
|
+
const { sessionMemory } = await import("./sessionMemory-HBBXUSIP.js");
|
|
462
462
|
const toolCalls = sessionMemory.getToolCallHistory(50);
|
|
463
463
|
let edgeCount = 0;
|
|
464
464
|
for (const call of toolCalls) {
|
|
@@ -586,8 +586,8 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
586
586
|
return lines.join("\n");
|
|
587
587
|
} catch {
|
|
588
588
|
try {
|
|
589
|
-
let walk2 = function(dir) {
|
|
590
|
-
if (results.length >= limit) return;
|
|
589
|
+
let walk2 = function(dir, depth = 0) {
|
|
590
|
+
if (results.length >= limit || depth > MAX_WALK_DEPTH) return;
|
|
591
591
|
let entries = [];
|
|
592
592
|
try {
|
|
593
593
|
entries = fs2.readdirSync(dir);
|
|
@@ -600,7 +600,7 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
600
600
|
try {
|
|
601
601
|
const stat = fs2.statSync(full);
|
|
602
602
|
if (stat.isDirectory()) {
|
|
603
|
-
walk2(full);
|
|
603
|
+
walk2(full, depth + 1);
|
|
604
604
|
} else if (entry.toLowerCase().includes(query.toLowerCase())) {
|
|
605
605
|
results.push(path2.relative(root, full));
|
|
606
606
|
}
|
|
@@ -613,7 +613,8 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
613
613
|
const path2 = await import("path");
|
|
614
614
|
const root = process.cwd();
|
|
615
615
|
const results = [];
|
|
616
|
-
|
|
616
|
+
const MAX_WALK_DEPTH = 8;
|
|
617
|
+
walk2(root, 0);
|
|
617
618
|
if (results.length === 0) {
|
|
618
619
|
return `\u{1F50D} **Codebase Search** \u2014 No results for "${query}". Try a different search term.`;
|
|
619
620
|
}
|
|
@@ -744,7 +745,7 @@ async function codebaseImpactFallback(target) {
|
|
|
744
745
|
const root = process.cwd();
|
|
745
746
|
const grepResult = execSync2(
|
|
746
747
|
`grep -rn --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.json" -l "${target.replace(/"/g, '\\"')}" . 2>/dev/null | grep -v node_modules | grep -v .git | grep -v dist | grep -v .kuma | head -50`,
|
|
747
|
-
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer:
|
|
748
|
+
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
748
749
|
).trim();
|
|
749
750
|
const files = grepResult ? grepResult.split("\n").filter(Boolean).filter((f) => !f.startsWith("./")) : [];
|
|
750
751
|
if (files.length === 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sessionMemory
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BI7KD3SG.js";
|
|
4
4
|
import {
|
|
5
5
|
getProjectRoot
|
|
6
6
|
} from "./chunk-E2KFPEBT.js";
|
|
@@ -82,7 +82,7 @@ function recordDecision(decision) {
|
|
|
82
82
|
}
|
|
83
83
|
async function recordDecisionToGraph(decision) {
|
|
84
84
|
try {
|
|
85
|
-
const { upsertNode, addEdge } = await import("./kumaGraph-
|
|
85
|
+
const { upsertNode, addEdge } = await import("./kumaGraph-35TAIBWD.js");
|
|
86
86
|
const decisionId = `decision::${decision.title.replace(/[^a-zA-Z0-9_\-\s]/g, "").trim().replace(/\s+/g, "-")}`;
|
|
87
87
|
await upsertNode({
|
|
88
88
|
id: decisionId,
|
package/dist/index.js
CHANGED
|
@@ -14,13 +14,22 @@ import {
|
|
|
14
14
|
getGraphStats,
|
|
15
15
|
searchGraph,
|
|
16
16
|
traceFlow
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-K64NSHBR.js";
|
|
18
|
+
import {
|
|
19
|
+
formatDecisionTemplate,
|
|
20
|
+
getProactiveMemories,
|
|
21
|
+
recordDecision,
|
|
22
|
+
scoreMemoryRelevance
|
|
23
|
+
} from "./chunk-X5TPBDKO.js";
|
|
18
24
|
import {
|
|
19
25
|
buildDriftMessages,
|
|
20
26
|
getGitDiffStat,
|
|
21
27
|
getSessionStats,
|
|
22
28
|
getUnresolvedCount
|
|
23
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-FOQQ2CSL.js";
|
|
30
|
+
import {
|
|
31
|
+
sessionMemory
|
|
32
|
+
} from "./chunk-BI7KD3SG.js";
|
|
24
33
|
import {
|
|
25
34
|
addContextNote,
|
|
26
35
|
addSecurityFinding,
|
|
@@ -46,16 +55,7 @@ import {
|
|
|
46
55
|
saveResearchCache,
|
|
47
56
|
updateDecisionStatus,
|
|
48
57
|
updateTodoStatus
|
|
49
|
-
} from "./chunk-
|
|
50
|
-
import {
|
|
51
|
-
formatDecisionTemplate,
|
|
52
|
-
getProactiveMemories,
|
|
53
|
-
recordDecision,
|
|
54
|
-
scoreMemoryRelevance
|
|
55
|
-
} from "./chunk-6QX4LBHV.js";
|
|
56
|
-
import {
|
|
57
|
-
sessionMemory
|
|
58
|
-
} from "./chunk-PH4JKL6T.js";
|
|
58
|
+
} from "./chunk-GDNAWLHF.js";
|
|
59
59
|
import {
|
|
60
60
|
getProjectRoot,
|
|
61
61
|
validateFilePath
|
|
@@ -87,7 +87,7 @@ var CONTEXT_ALIASES = {
|
|
|
87
87
|
"refactor": "impact",
|
|
88
88
|
// Navigate synonyms
|
|
89
89
|
"trace": "navigate",
|
|
90
|
-
"flow": "navigate",
|
|
90
|
+
"flow-trace": "navigate",
|
|
91
91
|
"navigate": "navigate",
|
|
92
92
|
// Init synonyms
|
|
93
93
|
"init": "init",
|
|
@@ -111,7 +111,20 @@ var CONTEXT_ALIASES = {
|
|
|
111
111
|
// Researches synonyms
|
|
112
112
|
"researches": "researches",
|
|
113
113
|
"research-list": "researches",
|
|
114
|
-
"list-research": "researches"
|
|
114
|
+
"list-research": "researches",
|
|
115
|
+
// Sync/Batch synonyms (Issue #12)
|
|
116
|
+
"sync": "sync",
|
|
117
|
+
"batch": "sync",
|
|
118
|
+
"kuma_sync": "sync",
|
|
119
|
+
"unified": "sync",
|
|
120
|
+
"state": "sync",
|
|
121
|
+
// Visualize synonyms (Issue #16)
|
|
122
|
+
"visualize": "visualize",
|
|
123
|
+
"graph": "visualize",
|
|
124
|
+
"diagram": "visualize",
|
|
125
|
+
"flow": "visualize",
|
|
126
|
+
"viz": "visualize",
|
|
127
|
+
"kuma_visualize": "visualize"
|
|
115
128
|
};
|
|
116
129
|
async function handleContext(params) {
|
|
117
130
|
const rawAction = params.action || "init";
|
|
@@ -134,8 +147,12 @@ async function handleContext(params) {
|
|
|
134
147
|
return handleResearches(params);
|
|
135
148
|
case "health":
|
|
136
149
|
return handleHealth(params);
|
|
150
|
+
case "sync":
|
|
151
|
+
return handleSync(params);
|
|
152
|
+
case "visualize":
|
|
153
|
+
return handleVisualize(params);
|
|
137
154
|
default:
|
|
138
|
-
return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health`;
|
|
155
|
+
return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health, sync, visualize`;
|
|
139
156
|
}
|
|
140
157
|
}
|
|
141
158
|
async function handleInit(_params) {
|
|
@@ -196,8 +213,8 @@ async function handleInit(_params) {
|
|
|
196
213
|
lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
|
|
197
214
|
lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
|
|
198
215
|
try {
|
|
199
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
200
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
216
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
|
|
217
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
|
|
201
218
|
const score = await computeSafetyScore(_params.goal);
|
|
202
219
|
const checksStr = JSON.stringify(score.checks);
|
|
203
220
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -370,8 +387,8 @@ async function handleResearches(_params) {
|
|
|
370
387
|
async function handleHealth(_params) {
|
|
371
388
|
sessionMemory.recordToolCall("kuma_context_health", {});
|
|
372
389
|
try {
|
|
373
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
374
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
390
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
|
|
391
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
|
|
375
392
|
const score = await computeSafetyScore();
|
|
376
393
|
const checksStr = JSON.stringify(score.checks);
|
|
377
394
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -380,6 +397,64 @@ async function handleHealth(_params) {
|
|
|
380
397
|
return `Error computing health: ${err}`;
|
|
381
398
|
}
|
|
382
399
|
}
|
|
400
|
+
async function handleSync(params) {
|
|
401
|
+
sessionMemory.setGoal(params.goal || "Sync session");
|
|
402
|
+
sessionMemory.recordToolCall("kuma_context_sync", { goal: params.goal });
|
|
403
|
+
const lines = [
|
|
404
|
+
"\u{1F504} **Kuma Sync \u2014 Unified State**",
|
|
405
|
+
`\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`,
|
|
406
|
+
"",
|
|
407
|
+
`\u{1F4C1} Project: ${getProjectRoot().split("/").pop() || "unknown"}`,
|
|
408
|
+
`\u{1F550} Session: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
409
|
+
""
|
|
410
|
+
];
|
|
411
|
+
const summary = sessionMemory.getSummary();
|
|
412
|
+
lines.push("**Session State**");
|
|
413
|
+
lines.push(` \u{1F3AF} Goal: ${summary.currentGoal || "not set"}`);
|
|
414
|
+
lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
|
|
415
|
+
lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
|
|
416
|
+
lines.push("");
|
|
417
|
+
try {
|
|
418
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
|
|
419
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
|
|
420
|
+
const score = await computeSafetyScore(params.goal);
|
|
421
|
+
const checksStr = JSON.stringify(score.checks);
|
|
422
|
+
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
423
|
+
lines.push("**Health Score**");
|
|
424
|
+
lines.push(formatSafetyScore(score));
|
|
425
|
+
} catch {
|
|
426
|
+
lines.push("**Health Score**: \u26A0\uFE0F Could not compute");
|
|
427
|
+
}
|
|
428
|
+
lines.push("");
|
|
429
|
+
try {
|
|
430
|
+
const { getGraphStats: getGraphStats2 } = await import("./kumaGraph-35TAIBWD.js");
|
|
431
|
+
lines.push("**Knowledge Graph**");
|
|
432
|
+
lines.push(await getGraphStats2());
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
lines.push("");
|
|
436
|
+
try {
|
|
437
|
+
const { getProactiveMemories: getProactiveMemories2 } = await import("./kumaMemory-5SR3TGRL.js");
|
|
438
|
+
const memories = getProactiveMemories2();
|
|
439
|
+
if (memories) {
|
|
440
|
+
lines.push("**Relevant Memories**");
|
|
441
|
+
lines.push(memories);
|
|
442
|
+
lines.push("");
|
|
443
|
+
}
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
lines.push("\u{1F4A1} Sync complete \u2014 all state captured in a single roundtrip.");
|
|
447
|
+
return lines.join("\n");
|
|
448
|
+
}
|
|
449
|
+
async function handleVisualize(params) {
|
|
450
|
+
const scope = params.scope;
|
|
451
|
+
const { generateVisualizeReport } = await import("./kumaVisualize-ODCWFSUY.js");
|
|
452
|
+
return await generateVisualizeReport({
|
|
453
|
+
scope,
|
|
454
|
+
type: "flowchart",
|
|
455
|
+
maxNodes: 40
|
|
456
|
+
});
|
|
457
|
+
}
|
|
383
458
|
function computeProjectHash(scope) {
|
|
384
459
|
try {
|
|
385
460
|
const root = getProjectRoot();
|
|
@@ -501,7 +576,7 @@ async function handleDecision(params) {
|
|
|
501
576
|
case "template":
|
|
502
577
|
return formatDecisionTemplate();
|
|
503
578
|
case "suggest": {
|
|
504
|
-
const { shouldRecordDecision } = await import("./kumaMemory-
|
|
579
|
+
const { shouldRecordDecision } = await import("./kumaMemory-5SR3TGRL.js");
|
|
505
580
|
const check = shouldRecordDecision();
|
|
506
581
|
return check.worth ? `\u{1F4A1} Decision suggested: "${check.title}"
|
|
507
582
|
Use kuma_memory({ action: 'decision', title: '...', context: '...', rationale: '...', outcome: '...' }) to record.` : "\u2705 No decision needed at this time.";
|
|
@@ -579,8 +654,17 @@ async function handleSearch(params) {
|
|
|
579
654
|
if (!query) return "\u26A0\uFE0F query or scope parameter required.";
|
|
580
655
|
const limit = params.limit || 20;
|
|
581
656
|
const memResults = sessionMemory.searchMemory(query, limit);
|
|
582
|
-
const { searchGraph: searchGraph2 } = await import("./kumaGraph-
|
|
657
|
+
const { searchGraph: searchGraph2 } = await import("./kumaGraph-35TAIBWD.js");
|
|
583
658
|
const graphResults = await searchGraph2(query, Math.min(limit, 10));
|
|
659
|
+
let hybridResults = "";
|
|
660
|
+
try {
|
|
661
|
+
const { hybridSearch, formatHybridResults } = await import("./kumaSearch-LG2OYEJY.js");
|
|
662
|
+
const semanticResults = await hybridSearch(query, 8);
|
|
663
|
+
if (semanticResults.length > 0) {
|
|
664
|
+
hybridResults = "\n" + formatHybridResults(query, semanticResults);
|
|
665
|
+
}
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
584
668
|
const lines = [`\u{1F50D} **Search Results** \u2014 "${query}"
|
|
585
669
|
\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
|
|
586
670
|
`];
|
|
@@ -590,6 +674,10 @@ async function handleSearch(params) {
|
|
|
590
674
|
lines.push("");
|
|
591
675
|
}
|
|
592
676
|
lines.push("**Knowledge Graph:**\n" + graphResults);
|
|
677
|
+
if (hybridResults) {
|
|
678
|
+
lines.push("");
|
|
679
|
+
lines.push(hybridResults);
|
|
680
|
+
}
|
|
593
681
|
return lines.join("\n");
|
|
594
682
|
}
|
|
595
683
|
async function handleChanges2(params) {
|
|
@@ -654,7 +742,7 @@ async function handleDecisionLog(params) {
|
|
|
654
742
|
}
|
|
655
743
|
async function handleMine(params) {
|
|
656
744
|
sessionMemory.recordToolCall("kuma_memory_mine", { scope: params.scope });
|
|
657
|
-
const { mineHistoricalDecisions } = await import("./kumaMiner-
|
|
745
|
+
const { mineHistoricalDecisions } = await import("./kumaMiner-P5KCUSF7.js");
|
|
658
746
|
return await mineHistoricalDecisions({
|
|
659
747
|
scope: params.scope,
|
|
660
748
|
since: typeof params.since === "string" ? params.since : void 0,
|
|
@@ -1791,7 +1879,7 @@ async function handleLock(params) {
|
|
|
1791
1879
|
}
|
|
1792
1880
|
async function handleHealth2(_params) {
|
|
1793
1881
|
try {
|
|
1794
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
1882
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
|
|
1795
1883
|
const score = await computeSafetyScore();
|
|
1796
1884
|
await saveHealthSnapshot(score.score, score.risk, JSON.stringify(score.checks), score.summary);
|
|
1797
1885
|
return formatSafetyScore(score);
|
|
@@ -1905,7 +1993,7 @@ async function handleVerify(params) {
|
|
|
1905
1993
|
}
|
|
1906
1994
|
_lastVerifyCall = now;
|
|
1907
1995
|
sessionMemory.recordToolCall("kuma_safety_verify", { scope: params.scope || params.filePath });
|
|
1908
|
-
const { runAutoVerification } = await import("./kumaVerifier-
|
|
1996
|
+
const { runAutoVerification } = await import("./kumaVerifier-LFRNIGSL.js");
|
|
1909
1997
|
return await runAutoVerification({
|
|
1910
1998
|
scope: params.scope || params.filePath,
|
|
1911
1999
|
force: params.force,
|
|
@@ -1919,7 +2007,7 @@ function registerAllTools(server) {
|
|
|
1919
2007
|
"kuma_context",
|
|
1920
2008
|
"**Call FIRST every session.** Understand your project before making changes. Actions: init (load project brief), research (5-step pipeline: cache\u2192staleness\u2192graph\u2192impact\u2192decision), impact (analyze change effects), navigate (trace code flow), changes (view change log), health (project health score 0-100). RESEARCH IS REQUIRED before editing unfamiliar code.",
|
|
1921
2009
|
{
|
|
1922
|
-
action: z.enum(["init", "research", "impact", "navigate", "changes", "health", "rollback", "researches"]).describe("Action: init=project brief, research=5-step research pipeline (REQUIRED before edits), impact=analyze change effects, navigate=trace code flow, changes=view change log, rollback=undo a change by ID, researches=list all cached research, health=project health score"),
|
|
2010
|
+
action: z.enum(["init", "research", "impact", "navigate", "changes", "health", "rollback", "researches", "sync", "visualize"]).describe("Action: init=project brief, research=5-step research pipeline (REQUIRED before edits), impact=analyze change effects, navigate=trace code flow, changes=view change log, rollback=undo a change by ID, researches=list all cached research, sync=unified batch state, visualize=Mermaid knowledge graph diagram, health=project health score"),
|
|
1923
2011
|
scope: z.string().optional().describe("Research scope for research action (e.g. 'auth', 'database', 'api')"),
|
|
1924
2012
|
target: z.string().optional().describe("Target symbol/file for impact/navigate/changes"),
|
|
1925
2013
|
goal: z.string().optional().describe("Current goal (for init/health)"),
|
|
@@ -2104,7 +2192,7 @@ async function main() {
|
|
|
2104
2192
|
console.error("");
|
|
2105
2193
|
let killedCount = 0;
|
|
2106
2194
|
try {
|
|
2107
|
-
const { getRunningVerificationPid } = await import("./kumaVerifier-
|
|
2195
|
+
const { getRunningVerificationPid } = await import("./kumaVerifier-LFRNIGSL.js");
|
|
2108
2196
|
const pid = getRunningVerificationPid();
|
|
2109
2197
|
if (pid) {
|
|
2110
2198
|
try {
|
|
@@ -2196,6 +2284,9 @@ async function main() {
|
|
|
2196
2284
|
console.error("");
|
|
2197
2285
|
process.exit(0);
|
|
2198
2286
|
}
|
|
2287
|
+
if (args[0] === "--hook") {
|
|
2288
|
+
process.exit(0);
|
|
2289
|
+
}
|
|
2199
2290
|
if (args[0] === "init") {
|
|
2200
2291
|
const flags = args.slice(1);
|
|
2201
2292
|
if (flags.includes("--help") || flags.includes("-h")) {
|
|
@@ -2343,7 +2434,7 @@ async function main() {
|
|
|
2343
2434
|
console.error(`[${SERVER_NAME}] \u2705 Restored session (${sessionInfo.toolCallCount} previous tool calls)`);
|
|
2344
2435
|
}
|
|
2345
2436
|
try {
|
|
2346
|
-
const { buildFromSessionMemory } = await import("./kumaGraph-
|
|
2437
|
+
const { buildFromSessionMemory } = await import("./kumaGraph-35TAIBWD.js");
|
|
2347
2438
|
const edgeCount = await buildFromSessionMemory();
|
|
2348
2439
|
if (edgeCount > 0) {
|
|
2349
2440
|
console.error(`[${SERVER_NAME}] \u2705 Graph auto-populated with ${edgeCount} entries from session memory`);
|
|
@@ -2363,7 +2454,7 @@ async function main() {
|
|
|
2363
2454
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Scratch directory setup: ${err}`);
|
|
2364
2455
|
}
|
|
2365
2456
|
try {
|
|
2366
|
-
const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-
|
|
2457
|
+
const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-DJUDLYBJ.js");
|
|
2367
2458
|
const db = await getDb2();
|
|
2368
2459
|
db.run(
|
|
2369
2460
|
`INSERT INTO sessions (started_at, goal, tool_calls) VALUES (?, ?, ?)`,
|
|
@@ -2373,6 +2464,44 @@ async function main() {
|
|
|
2373
2464
|
} catch (err) {
|
|
2374
2465
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Session DB record: ${err}`);
|
|
2375
2466
|
}
|
|
2467
|
+
try {
|
|
2468
|
+
const fs7 = await import("fs");
|
|
2469
|
+
const path7 = await import("path");
|
|
2470
|
+
const hooksDir = path7.resolve(process.cwd(), ".kuma", "hooks");
|
|
2471
|
+
if (!fs7.existsSync(hooksDir)) {
|
|
2472
|
+
fs7.mkdirSync(hooksDir, { recursive: true });
|
|
2473
|
+
const hookContent = `#!/bin/bash
|
|
2474
|
+
# Kuma auto-capture hook (post-commit)
|
|
2475
|
+
# Auto-updates knowledge graph after git commits
|
|
2476
|
+
if command -v npx &> /dev/null; then
|
|
2477
|
+
npx -y @plumpslabs/kuma --hook post-commit 2>/dev/null || true
|
|
2478
|
+
fi
|
|
2479
|
+
`;
|
|
2480
|
+
const gitHooksDir = path7.resolve(process.cwd(), ".git", "hooks");
|
|
2481
|
+
if (fs7.existsSync(gitHooksDir)) {
|
|
2482
|
+
const postCommitPath = path7.join(gitHooksDir, "post-commit");
|
|
2483
|
+
if (!fs7.existsSync(postCommitPath)) {
|
|
2484
|
+
fs7.writeFileSync(postCommitPath, hookContent, "utf-8");
|
|
2485
|
+
try {
|
|
2486
|
+
fs7.chmodSync(postCommitPath, 493);
|
|
2487
|
+
} catch {
|
|
2488
|
+
}
|
|
2489
|
+
console.error(`[${SERVER_NAME}] \u2705 Created .git/hooks/post-commit for auto-capture`);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
} catch (err) {
|
|
2494
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Auto-capture hook setup: ${err}`);
|
|
2495
|
+
}
|
|
2496
|
+
try {
|
|
2497
|
+
const { buildSearchVectors } = await import("./kumaSearch-LG2OYEJY.js");
|
|
2498
|
+
const vectors = await buildSearchVectors();
|
|
2499
|
+
if (vectors.length > 0) {
|
|
2500
|
+
console.error(`[${SERVER_NAME}] \u2705 Search vector cache pre-warmed (${vectors.length} documents)`);
|
|
2501
|
+
}
|
|
2502
|
+
} catch (err) {
|
|
2503
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Search vector cache: ${err}`);
|
|
2504
|
+
}
|
|
2376
2505
|
console.error(`[${SERVER_NAME}] \u2705 Cold start bootstrap complete`);
|
|
2377
2506
|
} catch (err) {
|
|
2378
2507
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Cold start bootstrap error: ${err}`);
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
recordDecision,
|
|
5
5
|
scoreMemoryRelevance,
|
|
6
6
|
shouldRecordDecision
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-X5TPBDKO.js";
|
|
8
|
+
import "./chunk-BI7KD3SG.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
export {
|
|
11
11
|
formatDecisionTemplate,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
recordDecisionLog
|
|
3
|
-
} from "./chunk-2NBESJTH.js";
|
|
4
1
|
import {
|
|
5
2
|
recordDecision
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-X5TPBDKO.js";
|
|
4
|
+
import "./chunk-BI7KD3SG.js";
|
|
5
|
+
import {
|
|
6
|
+
recordDecisionLog
|
|
7
|
+
} from "./chunk-GDNAWLHF.js";
|
|
8
8
|
import "./chunk-E2KFPEBT.js";
|
|
9
9
|
|
|
10
10
|
// src/engine/kumaMiner.ts
|
|
@@ -19,9 +19,10 @@ async function mineHistoricalDecisions(options = {}) {
|
|
|
19
19
|
try {
|
|
20
20
|
const sinceFlag = options.since ? `--since="${options.since}"` : `--since="1 year"`;
|
|
21
21
|
const gitCmd = `git log ${sinceFlag} -n 100 --pretty=format:"%h|%an|%ad|%s" --date=short`;
|
|
22
|
-
const gitOutput = execSync(gitCmd, { cwd: root, encoding: "utf-8" });
|
|
22
|
+
const gitOutput = execSync(gitCmd, { cwd: root, encoding: "utf-8", timeout: 15e3 });
|
|
23
23
|
const keywords = ["fix", "revert", "hack", "workaround", "urgent", "hotfix", "don't touch", "temporary", "deprecated", "workaround"];
|
|
24
24
|
const lines = gitOutput.split("\n").filter(Boolean);
|
|
25
|
+
const matchingHashes = [];
|
|
25
26
|
for (const line of lines) {
|
|
26
27
|
const parts = line.split("|");
|
|
27
28
|
if (parts.length < 4) continue;
|
|
@@ -29,9 +30,42 @@ async function mineHistoricalDecisions(options = {}) {
|
|
|
29
30
|
const lowerSubj = subject.toLowerCase();
|
|
30
31
|
const matchedKeyword = keywords.find((kw) => lowerSubj.includes(kw));
|
|
31
32
|
if (matchedKeyword) {
|
|
32
|
-
|
|
33
|
+
matchingHashes.push({ hash, author, date, subject, keyword: matchedKeyword });
|
|
34
|
+
if (matchingHashes.length >= limit) break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
let changedFilesMap = /* @__PURE__ */ new Map();
|
|
38
|
+
if (matchingHashes.length > 0) {
|
|
39
|
+
try {
|
|
40
|
+
const hashesStr = matchingHashes.map((h) => h.hash).join(" ");
|
|
41
|
+
const batchOutput = execSync(
|
|
42
|
+
`git show --name-only --oneline ${hashesStr} 2>/dev/null | head -200`,
|
|
43
|
+
{ cwd: root, encoding: "utf-8", timeout: 1e4, maxBuffer: 256 * 1024 }
|
|
44
|
+
);
|
|
45
|
+
let currentHash = "";
|
|
46
|
+
const files = [];
|
|
47
|
+
for (const line of batchOutput.split("\n")) {
|
|
48
|
+
if (matchingHashes.find((h) => line.startsWith(h.hash))) {
|
|
49
|
+
if (currentHash && files.length > 0) {
|
|
50
|
+
changedFilesMap.set(currentHash, files.slice(0, 3).join(", "));
|
|
51
|
+
}
|
|
52
|
+
currentHash = line.split(" ")[0];
|
|
53
|
+
files.length = 0;
|
|
54
|
+
} else if (line.trim() && !line.startsWith("diff")) {
|
|
55
|
+
files.push(line.trim());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (currentHash && files.length > 0) {
|
|
59
|
+
changedFilesMap.set(currentHash, files.slice(0, 3).join(", "));
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const { hash, author, date, subject, keyword: matchedKeyword } of matchingHashes) {
|
|
65
|
+
let changedFiles = changedFilesMap.get(hash) || "";
|
|
66
|
+
if (!changedFiles) {
|
|
33
67
|
try {
|
|
34
|
-
changedFiles = execSync(`git show --name-only --oneline ${hash}`, { cwd: root, encoding: "utf-8" }).split("\n").slice(1).filter(Boolean).slice(0, 3).join(", ");
|
|
68
|
+
changedFiles = execSync(`git show --name-only --oneline ${hash}`, { cwd: root, encoding: "utf-8", timeout: 5e3 }).split("\n").slice(1).filter(Boolean).slice(0, 3).join(", ");
|
|
35
69
|
} catch {
|
|
36
70
|
}
|
|
37
71
|
candidates.push({
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDb
|
|
3
|
+
} from "./chunk-GDNAWLHF.js";
|
|
4
|
+
import {
|
|
5
|
+
getProjectRoot
|
|
6
|
+
} from "./chunk-E2KFPEBT.js";
|
|
7
|
+
|
|
8
|
+
// src/engine/kumaSearch.ts
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
var SYNONYM_MAP = {
|
|
12
|
+
timeout: ["timeout", "duration", "expiry", "ttl", "deadline", "latency"],
|
|
13
|
+
duration: ["duration", "timeout", "length", "period", "interval"],
|
|
14
|
+
delay: ["delay", "wait", "latency", "defer", "throttle"],
|
|
15
|
+
latency: ["latency", "delay", "response time", "speed", "performance"],
|
|
16
|
+
error: ["error", "failure", "exception", "bug", "fault", "crash"],
|
|
17
|
+
crash: ["crash", "panic", "oom", "out of memory", "segfault"],
|
|
18
|
+
fail: ["fail", "error", "break", "regression", "broken"],
|
|
19
|
+
auth: ["auth", "authentication", "login", "oauth", "session", "token", "credential"],
|
|
20
|
+
login: ["login", "signin", "auth", "authenticate"],
|
|
21
|
+
token: ["token", "jwt", "session", "api key", "secret"],
|
|
22
|
+
permission: ["permission", "access", "role", "rbac", "authorization", "acl"],
|
|
23
|
+
database: ["database", "db", "storage", "persistence", "sql", "nosql"],
|
|
24
|
+
query: ["query", "search", "fetch", "select", "lookup"],
|
|
25
|
+
index: ["index", "key", "lookup", "search"],
|
|
26
|
+
cache: ["cache", "memcached", "redis", "buffer", "temporary storage"],
|
|
27
|
+
api: ["api", "endpoint", "route", "rest", "graphql", "service"],
|
|
28
|
+
route: ["route", "endpoint", "path", "handler"],
|
|
29
|
+
request: ["request", "http", "call", "fetch", "invocation"],
|
|
30
|
+
response: ["response", "reply", "result", "output"],
|
|
31
|
+
performance: ["performance", "speed", "latency", "throughput", "efficiency"],
|
|
32
|
+
memory: ["memory", "ram", "heap", "allocation", "leak"],
|
|
33
|
+
cpu: ["cpu", "processor", "compute", "thread", "worker"],
|
|
34
|
+
architecture: ["architecture", "design", "pattern", "structure", "system"],
|
|
35
|
+
service: ["service", "microservice", "module", "component", "layer"],
|
|
36
|
+
config: ["config", "configuration", "setting", "env", "environment"],
|
|
37
|
+
deploy: ["deploy", "release", "rollout", "publish", "ship"],
|
|
38
|
+
build: ["build", "compile", "bundle", "transpile", "package"],
|
|
39
|
+
test: ["test", "spec", "unit test", "integration", "e2e"],
|
|
40
|
+
lint: ["lint", "format", "style", "prettier", "eslint"],
|
|
41
|
+
redis: ["redis", "cache", "session store", "message queue"],
|
|
42
|
+
postgres: ["postgres", "postgresql", "psql", "relational db", "sql"],
|
|
43
|
+
docker: ["docker", "container", "image", "dockerfile", "compose"],
|
|
44
|
+
typescript: ["typescript", "ts", "type system", "compiler"],
|
|
45
|
+
prisma: ["prisma", "orm", "database client", "schema"],
|
|
46
|
+
create: ["create", "add", "new", "make", "generate"],
|
|
47
|
+
update: ["update", "modify", "change", "edit", "patch"],
|
|
48
|
+
delete: ["delete", "remove", "drop", "clear", "purge"]
|
|
49
|
+
};
|
|
50
|
+
function expandQueryTerms(query) {
|
|
51
|
+
const terms = query.toLowerCase().split(/[\s_-]+/).filter((t) => t.length > 2);
|
|
52
|
+
const expanded = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const term of terms) {
|
|
54
|
+
const sources = /* @__PURE__ */ new Set();
|
|
55
|
+
sources.add(term);
|
|
56
|
+
if (SYNONYM_MAP[term]) {
|
|
57
|
+
for (const syn of SYNONYM_MAP[term]) {
|
|
58
|
+
sources.add(syn);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const [key, synonyms] of Object.entries(SYNONYM_MAP)) {
|
|
62
|
+
if (synonyms.includes(term)) {
|
|
63
|
+
sources.add(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
expanded.set(term, sources);
|
|
67
|
+
}
|
|
68
|
+
return expanded;
|
|
69
|
+
}
|
|
70
|
+
var _vectorCache = null;
|
|
71
|
+
var VECTOR_CACHE_TTL = 3e5;
|
|
72
|
+
async function buildSearchVectors() {
|
|
73
|
+
if (_vectorCache && Date.now() - _vectorCache.builtAt < VECTOR_CACHE_TTL) {
|
|
74
|
+
return _vectorCache.vectors;
|
|
75
|
+
}
|
|
76
|
+
const vectors = [];
|
|
77
|
+
try {
|
|
78
|
+
const db = await getDb();
|
|
79
|
+
const stmt = db.prepare("SELECT id, name, metadata, file_path FROM nodes ORDER BY updated_at DESC LIMIT 500");
|
|
80
|
+
while (stmt.step()) {
|
|
81
|
+
const row = stmt.getAsObject();
|
|
82
|
+
const name = row.name || "";
|
|
83
|
+
const metaStr = row.metadata || "{}";
|
|
84
|
+
let metaText = "";
|
|
85
|
+
try {
|
|
86
|
+
const meta = JSON.parse(metaStr);
|
|
87
|
+
metaText = Object.values(meta).join(" ");
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
const filePath = row.file_path || "";
|
|
91
|
+
const nodeText = `${name} ${metaText} ${filePath}`.toLowerCase();
|
|
92
|
+
const rawTerms = extractTerms(nodeText);
|
|
93
|
+
const termCounts = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const t of rawTerms) {
|
|
95
|
+
termCounts.set(t, (termCounts.get(t) || 0) + 1);
|
|
96
|
+
}
|
|
97
|
+
vectors.push({
|
|
98
|
+
terms: termCounts,
|
|
99
|
+
source: name || row.id,
|
|
100
|
+
sourceType: "graph"
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
stmt.free();
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const root = getProjectRoot();
|
|
108
|
+
const memDir = path.join(root, ".kuma", "memories");
|
|
109
|
+
if (fs.existsSync(memDir)) {
|
|
110
|
+
const files = fs.readdirSync(memDir).filter((f) => f.endsWith(".md")).slice(0, 10);
|
|
111
|
+
for (const file of files) {
|
|
112
|
+
try {
|
|
113
|
+
const content = fs.readFileSync(path.join(memDir, file), "utf-8");
|
|
114
|
+
const text = content.toLowerCase();
|
|
115
|
+
const rawTerms = extractTerms(text);
|
|
116
|
+
const termCounts = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const t of rawTerms) {
|
|
118
|
+
termCounts.set(t, (termCounts.get(t) || 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
vectors.push({
|
|
121
|
+
terms: termCounts,
|
|
122
|
+
source: file.replace(/\.md$/, ""),
|
|
123
|
+
sourceType: "memory"
|
|
124
|
+
});
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const db = await getDb();
|
|
133
|
+
const stmt = db.prepare("SELECT scope, record FROM research_cache ORDER BY updated_at DESC LIMIT 50");
|
|
134
|
+
while (stmt.step()) {
|
|
135
|
+
const row = stmt.getAsObject();
|
|
136
|
+
const scope = row.scope || "";
|
|
137
|
+
const record = row.record || "";
|
|
138
|
+
const text = `${scope} ${record}`.toLowerCase();
|
|
139
|
+
const rawTerms = extractTerms(text);
|
|
140
|
+
const termCounts = /* @__PURE__ */ new Map();
|
|
141
|
+
for (const t of rawTerms) {
|
|
142
|
+
termCounts.set(t, (termCounts.get(t) || 0) + 1);
|
|
143
|
+
}
|
|
144
|
+
vectors.push({
|
|
145
|
+
terms: termCounts,
|
|
146
|
+
source: scope,
|
|
147
|
+
sourceType: "research"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
stmt.free();
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
const docCount = vectors.length || 1;
|
|
154
|
+
const docFreq = /* @__PURE__ */ new Map();
|
|
155
|
+
for (const vec of vectors) {
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
for (const [term] of vec.terms) {
|
|
158
|
+
if (!seen.has(term)) {
|
|
159
|
+
docFreq.set(term, (docFreq.get(term) || 0) + 1);
|
|
160
|
+
seen.add(term);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (const vec of vectors) {
|
|
165
|
+
const maxFreq = Math.max(1, ...Array.from(vec.terms.values()));
|
|
166
|
+
for (const [term, rawFreq] of vec.terms) {
|
|
167
|
+
const tf = 0.5 + 0.5 * rawFreq / maxFreq;
|
|
168
|
+
const docsWithTerm = docFreq.get(term) || 1;
|
|
169
|
+
const idf = Math.log(1 + (docCount - docsWithTerm + 0.5) / (docsWithTerm + 0.5));
|
|
170
|
+
vec.terms.set(term, tf * idf);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
_vectorCache = { vectors, builtAt: Date.now() };
|
|
174
|
+
return vectors;
|
|
175
|
+
}
|
|
176
|
+
function extractTerms(text) {
|
|
177
|
+
return text.toLowerCase().split(/[\s,.;:!?()\[\]{}"'\/\\|@#$%^&*+=<>~`]+/).filter((t) => t.length > 2 && t.length < 50 && !/^\d+$/.test(t));
|
|
178
|
+
}
|
|
179
|
+
function cosineSimilarity(queryVec, docVec) {
|
|
180
|
+
let dotProduct = 0;
|
|
181
|
+
let queryMagnitude = 0;
|
|
182
|
+
let docMagnitude = 0;
|
|
183
|
+
for (const [term, qWeight] of queryVec) {
|
|
184
|
+
queryMagnitude += qWeight * qWeight;
|
|
185
|
+
const dWeight = docVec.get(term) || 0;
|
|
186
|
+
dotProduct += qWeight * dWeight;
|
|
187
|
+
}
|
|
188
|
+
for (const [, dWeight] of docVec) {
|
|
189
|
+
docMagnitude += dWeight * dWeight;
|
|
190
|
+
}
|
|
191
|
+
const mag = Math.sqrt(queryMagnitude) * Math.sqrt(docMagnitude);
|
|
192
|
+
if (mag === 0) return 0;
|
|
193
|
+
return dotProduct / mag;
|
|
194
|
+
}
|
|
195
|
+
async function hybridSearch(query, limit = 10) {
|
|
196
|
+
const results = [];
|
|
197
|
+
const expandedTerms = expandQueryTerms(query);
|
|
198
|
+
const allSearchTerms = /* @__PURE__ */ new Set();
|
|
199
|
+
for (const [, sources] of expandedTerms) {
|
|
200
|
+
for (const term of sources) {
|
|
201
|
+
allSearchTerms.add(term);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const searchTerms = Array.from(allSearchTerms);
|
|
205
|
+
const queryVector = /* @__PURE__ */ new Map();
|
|
206
|
+
for (const [originalTerm, sources] of expandedTerms) {
|
|
207
|
+
for (const term of sources) {
|
|
208
|
+
const weight = term === originalTerm ? 2 : 1;
|
|
209
|
+
queryVector.set(term, (queryVector.get(term) || 0) + weight);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const db = await getDb();
|
|
214
|
+
const likeClauses = searchTerms.map(() => `name LIKE ?`).join(" OR ");
|
|
215
|
+
const sql = `SELECT id, name, type, file_path, metadata FROM nodes WHERE (${likeClauses}) LIMIT ${limit * 3}`;
|
|
216
|
+
const stmt = db.prepare(sql);
|
|
217
|
+
stmt.bind(searchTerms.map((t) => `%${t}%`));
|
|
218
|
+
const seen = /* @__PURE__ */ new Set();
|
|
219
|
+
while (stmt.step()) {
|
|
220
|
+
const row = stmt.getAsObject();
|
|
221
|
+
const name = row.name || "";
|
|
222
|
+
const filePath = row.file_path || "";
|
|
223
|
+
const sourceKey = name + filePath;
|
|
224
|
+
if (seen.has(sourceKey)) continue;
|
|
225
|
+
seen.add(sourceKey);
|
|
226
|
+
const lowerText = `${name} ${filePath}`.toLowerCase();
|
|
227
|
+
const matchedTerms = searchTerms.filter((t) => lowerText.includes(t));
|
|
228
|
+
const keywordScore = matchedTerms.length > 0 ? Math.round(matchedTerms.length / Math.max(1, searchTerms.length) * 100) : 0;
|
|
229
|
+
results.push({
|
|
230
|
+
source: name,
|
|
231
|
+
sourceType: "graph",
|
|
232
|
+
score: keywordScore,
|
|
233
|
+
keywordScore,
|
|
234
|
+
vectorScore: 0,
|
|
235
|
+
matchedTerms
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
stmt.free();
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error(`[KumaSearch] DB search failed: ${err}`);
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const vectors = await buildSearchVectors();
|
|
244
|
+
for (const vec of vectors) {
|
|
245
|
+
const vectorScore = Math.round(cosineSimilarity(queryVector, vec.terms) * 100);
|
|
246
|
+
const existing = results.find((r) => r.source === vec.source);
|
|
247
|
+
if (existing) {
|
|
248
|
+
existing.vectorScore = vectorScore;
|
|
249
|
+
existing.score = Math.min(100, Math.round(existing.keywordScore * 0.6 + vectorScore * 0.4));
|
|
250
|
+
for (const [term] of vec.terms) {
|
|
251
|
+
if (queryVector.has(term) && !existing.matchedTerms.includes(term)) {
|
|
252
|
+
existing.matchedTerms.push(term);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} else if (vectorScore > 0) {
|
|
256
|
+
const matchedTerms = [];
|
|
257
|
+
for (const [term] of vec.terms) {
|
|
258
|
+
if (queryVector.has(term)) matchedTerms.push(term);
|
|
259
|
+
}
|
|
260
|
+
results.push({
|
|
261
|
+
source: vec.source,
|
|
262
|
+
sourceType: vec.sourceType,
|
|
263
|
+
score: Math.round(vectorScore * 0.4),
|
|
264
|
+
keywordScore: 0,
|
|
265
|
+
vectorScore,
|
|
266
|
+
matchedTerms
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.error(`[KumaSearch] Vector search failed: ${err}`);
|
|
272
|
+
}
|
|
273
|
+
const uniqueResults = Array.from(
|
|
274
|
+
new Map(results.map((r) => [r.source, r])).values()
|
|
275
|
+
);
|
|
276
|
+
return uniqueResults.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
277
|
+
}
|
|
278
|
+
function formatHybridResults(query, results) {
|
|
279
|
+
const expandedTerms = expandQueryTerms(query);
|
|
280
|
+
const originalTerms = Array.from(expandedTerms.keys()).join(", ");
|
|
281
|
+
const totalSynonyms = Array.from(expandedTerms.values()).reduce(
|
|
282
|
+
(sum, s) => sum + s.size,
|
|
283
|
+
0
|
|
284
|
+
);
|
|
285
|
+
const lines = [
|
|
286
|
+
"**Hybrid Search** \u2014 " + query,
|
|
287
|
+
"----------------------------------------",
|
|
288
|
+
"",
|
|
289
|
+
"Expanded query: " + originalTerms + " (" + totalSynonyms + " total terms with synonyms)",
|
|
290
|
+
""
|
|
291
|
+
];
|
|
292
|
+
if (results.length === 0) {
|
|
293
|
+
lines.push("No results found. Try different keywords or research first.");
|
|
294
|
+
return lines.join("\n");
|
|
295
|
+
}
|
|
296
|
+
lines.push(results.length + " result(s) \u2014 ranked by hybrid relevance");
|
|
297
|
+
lines.push("");
|
|
298
|
+
for (let i = 0; i < results.length; i++) {
|
|
299
|
+
const r = results[i];
|
|
300
|
+
const typeIcon = r.sourceType === "graph" ? "file" : r.sourceType === "memory" ? "memory" : "research";
|
|
301
|
+
const matchType = r.vectorScore > r.keywordScore ? "semantic match" : r.keywordScore > 0 ? "keyword match" : "expanded match";
|
|
302
|
+
lines.push(i + 1 + ". [" + typeIcon + "] " + r.source + " \u2014 " + r.score + "%");
|
|
303
|
+
lines.push(" keyword: " + r.keywordScore + "% | vector: " + r.vectorScore + "% (" + matchType + ")");
|
|
304
|
+
if (r.matchedTerms.length > 0) {
|
|
305
|
+
lines.push(" Terms: " + r.matchedTerms.slice(0, 6).join(", "));
|
|
306
|
+
}
|
|
307
|
+
lines.push("");
|
|
308
|
+
}
|
|
309
|
+
lines.push("Hybrid search combines keyword matching with semantic synonym expansion.");
|
|
310
|
+
return lines.join("\n");
|
|
311
|
+
}
|
|
312
|
+
function clearSearchCache() {
|
|
313
|
+
_vectorCache = null;
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
buildSearchVectors,
|
|
317
|
+
clearSearchCache,
|
|
318
|
+
expandQueryTerms,
|
|
319
|
+
formatHybridResults,
|
|
320
|
+
hybridSearch
|
|
321
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sessionMemory
|
|
3
|
+
} from "./chunk-BI7KD3SG.js";
|
|
1
4
|
import {
|
|
2
5
|
getLatestVerifications,
|
|
3
6
|
saveVerification
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
sessionMemory
|
|
7
|
-
} from "./chunk-PH4JKL6T.js";
|
|
7
|
+
} from "./chunk-GDNAWLHF.js";
|
|
8
8
|
import "./chunk-E2KFPEBT.js";
|
|
9
9
|
|
|
10
10
|
// src/engine/kumaVerifier.ts
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDb
|
|
3
|
+
} from "./chunk-GDNAWLHF.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-FOQQ2CSL.js";
|
|
6
6
|
import {
|
|
7
7
|
sessionMemory
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BI7KD3SG.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
|
|
11
11
|
// src/engine/safetyScore.ts
|
|
@@ -15,7 +15,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
15
15
|
let totalScore = 0;
|
|
16
16
|
const maxScore = 100;
|
|
17
17
|
const gitStat = getGitDiffStat();
|
|
18
|
-
if (gitStat) {
|
|
18
|
+
if (gitStat && gitStat !== "clean") {
|
|
19
19
|
const lines = gitStat.split("\n").filter(Boolean).length;
|
|
20
20
|
if (lines === 0) {
|
|
21
21
|
checks.push({
|
|
@@ -42,6 +42,14 @@ async function computeSafetyScore(inputGoal) {
|
|
|
42
42
|
});
|
|
43
43
|
totalScore += 10;
|
|
44
44
|
}
|
|
45
|
+
} else if (gitStat === "clean") {
|
|
46
|
+
checks.push({
|
|
47
|
+
label: "Git Clean",
|
|
48
|
+
status: "pass",
|
|
49
|
+
message: "Working tree is clean",
|
|
50
|
+
weight: 20
|
|
51
|
+
});
|
|
52
|
+
totalScore += 20;
|
|
45
53
|
} else {
|
|
46
54
|
checks.push({
|
|
47
55
|
label: "Git Status",
|
|
@@ -52,7 +60,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
52
60
|
totalScore += 20;
|
|
53
61
|
}
|
|
54
62
|
try {
|
|
55
|
-
const { getDb } = await import("./kumaDb-
|
|
63
|
+
const { getDb } = await import("./kumaDb-DJUDLYBJ.js");
|
|
56
64
|
const db = await getDb();
|
|
57
65
|
const nodeCount = db.exec("SELECT COUNT(*) as c FROM nodes")[0]?.values[0][0] ?? 0;
|
|
58
66
|
const edgeCount = db.exec("SELECT COUNT(*) as c FROM edges")[0]?.values[0][0] ?? 0;
|
|
@@ -83,7 +91,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
83
91
|
totalScore += 5;
|
|
84
92
|
}
|
|
85
93
|
try {
|
|
86
|
-
const { getDb } = await import("./kumaDb-
|
|
94
|
+
const { getDb } = await import("./kumaDb-DJUDLYBJ.js");
|
|
87
95
|
const db = await getDb();
|
|
88
96
|
const researchCount = db.exec("SELECT COUNT(*) as c FROM research_cache")[0]?.values[0][0] ?? 0;
|
|
89
97
|
checks.push({
|
|
@@ -108,7 +116,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
108
116
|
const hasRunTests = stats.hasRunTests;
|
|
109
117
|
let latestVerif = null;
|
|
110
118
|
try {
|
|
111
|
-
const { getLatestVerifications } = await import("./kumaDb-
|
|
119
|
+
const { getLatestVerifications } = await import("./kumaDb-DJUDLYBJ.js");
|
|
112
120
|
const verifs = await getLatestVerifications(1);
|
|
113
121
|
if (verifs.length > 0) latestVerif = verifs[0];
|
|
114
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.7",
|
|
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",
|