@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
package/dist/index.js
CHANGED
|
@@ -4,6 +4,11 @@ import {
|
|
|
4
4
|
formatInitResults,
|
|
5
5
|
runInit
|
|
6
6
|
} from "./chunk-L5WU2HTN.js";
|
|
7
|
+
import {
|
|
8
|
+
auditStats,
|
|
9
|
+
queryAudit,
|
|
10
|
+
recordAudit
|
|
11
|
+
} from "./chunk-PTEPJK6M.js";
|
|
7
12
|
import {
|
|
8
13
|
analyzeImpact,
|
|
9
14
|
autoHeal,
|
|
@@ -14,13 +19,22 @@ import {
|
|
|
14
19
|
getGraphStats,
|
|
15
20
|
searchGraph,
|
|
16
21
|
traceFlow
|
|
17
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-4OB3XMHT.js";
|
|
23
|
+
import {
|
|
24
|
+
formatDecisionTemplate,
|
|
25
|
+
getProactiveMemories,
|
|
26
|
+
recordDecision,
|
|
27
|
+
scoreMemoryRelevance
|
|
28
|
+
} from "./chunk-RTGLQDMI.js";
|
|
18
29
|
import {
|
|
19
30
|
buildDriftMessages,
|
|
20
31
|
getGitDiffStat,
|
|
21
32
|
getSessionStats,
|
|
22
33
|
getUnresolvedCount
|
|
23
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-FL2TLLMX.js";
|
|
35
|
+
import {
|
|
36
|
+
sessionMemory
|
|
37
|
+
} from "./chunk-SLRRDKQ2.js";
|
|
24
38
|
import {
|
|
25
39
|
addContextNote,
|
|
26
40
|
addSecurityFinding,
|
|
@@ -41,21 +55,11 @@ import {
|
|
|
41
55
|
runDoctor,
|
|
42
56
|
runGarbageCollection,
|
|
43
57
|
saveBenchmark,
|
|
44
|
-
saveDb,
|
|
45
58
|
saveHealthSnapshot,
|
|
46
59
|
saveResearchCache,
|
|
47
60
|
updateDecisionStatus,
|
|
48
61
|
updateTodoStatus
|
|
49
|
-
} from "./chunk-
|
|
50
|
-
import {
|
|
51
|
-
formatDecisionTemplate,
|
|
52
|
-
getProactiveMemories,
|
|
53
|
-
recordDecision,
|
|
54
|
-
scoreMemoryRelevance
|
|
55
|
-
} from "./chunk-X5TPBDKO.js";
|
|
56
|
-
import {
|
|
57
|
-
sessionMemory
|
|
58
|
-
} from "./chunk-BI7KD3SG.js";
|
|
62
|
+
} from "./chunk-NAM7SCBT.js";
|
|
59
63
|
import {
|
|
60
64
|
getProjectRoot,
|
|
61
65
|
validateFilePath
|
|
@@ -87,7 +91,7 @@ var CONTEXT_ALIASES = {
|
|
|
87
91
|
"refactor": "impact",
|
|
88
92
|
// Navigate synonyms
|
|
89
93
|
"trace": "navigate",
|
|
90
|
-
"flow": "navigate",
|
|
94
|
+
"flow-trace": "navigate",
|
|
91
95
|
"navigate": "navigate",
|
|
92
96
|
// Init synonyms
|
|
93
97
|
"init": "init",
|
|
@@ -117,7 +121,26 @@ var CONTEXT_ALIASES = {
|
|
|
117
121
|
"batch": "sync",
|
|
118
122
|
"kuma_sync": "sync",
|
|
119
123
|
"unified": "sync",
|
|
120
|
-
"state": "sync"
|
|
124
|
+
"state": "sync",
|
|
125
|
+
// Visualize synonyms (Issue #16)
|
|
126
|
+
"visualize": "visualize",
|
|
127
|
+
"graph": "visualize",
|
|
128
|
+
"diagram": "visualize",
|
|
129
|
+
"flow": "visualize",
|
|
130
|
+
"viz": "visualize",
|
|
131
|
+
"kuma_visualize": "visualize",
|
|
132
|
+
// Digest synonyms (Issue #18)
|
|
133
|
+
"digest": "digest",
|
|
134
|
+
"bootstrap": "digest",
|
|
135
|
+
"kuma_digest": "digest",
|
|
136
|
+
"kuma_bootstrap": "digest",
|
|
137
|
+
"briefing": "digest",
|
|
138
|
+
"overview": "digest",
|
|
139
|
+
// Drift detection synonyms (Issue #20)
|
|
140
|
+
"drift": "drift",
|
|
141
|
+
"staleness": "drift",
|
|
142
|
+
"code-drift": "drift",
|
|
143
|
+
"freshness": "drift"
|
|
121
144
|
};
|
|
122
145
|
async function handleContext(params) {
|
|
123
146
|
const rawAction = params.action || "init";
|
|
@@ -142,8 +165,14 @@ async function handleContext(params) {
|
|
|
142
165
|
return handleHealth(params);
|
|
143
166
|
case "sync":
|
|
144
167
|
return handleSync(params);
|
|
168
|
+
case "visualize":
|
|
169
|
+
return handleVisualize(params);
|
|
170
|
+
case "digest":
|
|
171
|
+
return handleDigest(params);
|
|
172
|
+
case "drift":
|
|
173
|
+
return handleDrift(params);
|
|
145
174
|
default:
|
|
146
|
-
return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health, sync`;
|
|
175
|
+
return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health, sync, visualize, digest, drift`;
|
|
147
176
|
}
|
|
148
177
|
}
|
|
149
178
|
async function handleInit(_params) {
|
|
@@ -204,8 +233,8 @@ async function handleInit(_params) {
|
|
|
204
233
|
lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
|
|
205
234
|
lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
|
|
206
235
|
try {
|
|
207
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
208
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
236
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-GR5N55CU.js");
|
|
237
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-6D53ERFP.js");
|
|
209
238
|
const score = await computeSafetyScore(_params.goal);
|
|
210
239
|
const checksStr = JSON.stringify(score.checks);
|
|
211
240
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -378,8 +407,8 @@ async function handleResearches(_params) {
|
|
|
378
407
|
async function handleHealth(_params) {
|
|
379
408
|
sessionMemory.recordToolCall("kuma_context_health", {});
|
|
380
409
|
try {
|
|
381
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
382
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
410
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-GR5N55CU.js");
|
|
411
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-6D53ERFP.js");
|
|
383
412
|
const score = await computeSafetyScore();
|
|
384
413
|
const checksStr = JSON.stringify(score.checks);
|
|
385
414
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -406,8 +435,8 @@ async function handleSync(params) {
|
|
|
406
435
|
lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
|
|
407
436
|
lines.push("");
|
|
408
437
|
try {
|
|
409
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
410
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
438
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-GR5N55CU.js");
|
|
439
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-6D53ERFP.js");
|
|
411
440
|
const score = await computeSafetyScore(params.goal);
|
|
412
441
|
const checksStr = JSON.stringify(score.checks);
|
|
413
442
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -418,14 +447,14 @@ async function handleSync(params) {
|
|
|
418
447
|
}
|
|
419
448
|
lines.push("");
|
|
420
449
|
try {
|
|
421
|
-
const { getGraphStats: getGraphStats2 } = await import("./kumaGraph-
|
|
450
|
+
const { getGraphStats: getGraphStats2 } = await import("./kumaGraph-AWP743TJ.js");
|
|
422
451
|
lines.push("**Knowledge Graph**");
|
|
423
452
|
lines.push(await getGraphStats2());
|
|
424
453
|
} catch {
|
|
425
454
|
}
|
|
426
455
|
lines.push("");
|
|
427
456
|
try {
|
|
428
|
-
const { getProactiveMemories: getProactiveMemories2 } = await import("./kumaMemory-
|
|
457
|
+
const { getProactiveMemories: getProactiveMemories2 } = await import("./kumaMemory-7TCUDVZX.js");
|
|
429
458
|
const memories = getProactiveMemories2();
|
|
430
459
|
if (memories) {
|
|
431
460
|
lines.push("**Relevant Memories**");
|
|
@@ -437,6 +466,40 @@ async function handleSync(params) {
|
|
|
437
466
|
lines.push("\u{1F4A1} Sync complete \u2014 all state captured in a single roundtrip.");
|
|
438
467
|
return lines.join("\n");
|
|
439
468
|
}
|
|
469
|
+
async function handleVisualize(params) {
|
|
470
|
+
const scope = params.scope;
|
|
471
|
+
const { generateVisualizeReport } = await import("./kumaVisualize-TECABHUY.js");
|
|
472
|
+
return await generateVisualizeReport({
|
|
473
|
+
scope,
|
|
474
|
+
type: "flowchart",
|
|
475
|
+
maxNodes: 40
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
async function handleDigest(_params) {
|
|
479
|
+
sessionMemory.recordToolCall("kuma_context_digest", {});
|
|
480
|
+
try {
|
|
481
|
+
const { generateContextDigest } = await import("./contextDigest-4PCK3DSM.js");
|
|
482
|
+
return await generateContextDigest();
|
|
483
|
+
} catch (err) {
|
|
484
|
+
try {
|
|
485
|
+
const { generateDigest } = await import("./domainRules-LDZPOIGZ.js");
|
|
486
|
+
return generateDigest();
|
|
487
|
+
} catch {
|
|
488
|
+
return `Error generating digest: ${err}`;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async function handleDrift(_params) {
|
|
493
|
+
sessionMemory.recordToolCall("kuma_context_drift", {});
|
|
494
|
+
try {
|
|
495
|
+
const { detectDrift, formatDriftReport, flagStaleRecords } = await import("./kumaDriftDetector-OESI5GTC.js");
|
|
496
|
+
await flagStaleRecords();
|
|
497
|
+
const records = await detectDrift();
|
|
498
|
+
return formatDriftReport(records);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
return `Error detecting drift: ${err}`;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
440
503
|
function computeProjectHash(scope) {
|
|
441
504
|
try {
|
|
442
505
|
const root = getProjectRoot();
|
|
@@ -518,7 +581,27 @@ var MEMORY_ALIASES = {
|
|
|
518
581
|
// Changes synonyms
|
|
519
582
|
"changes": "changes",
|
|
520
583
|
"change-log": "changes",
|
|
521
|
-
"history": "changes"
|
|
584
|
+
"history": "changes",
|
|
585
|
+
// Layer 1: Domain Rules (Issue #17)
|
|
586
|
+
"domain_rules": "domain_rules",
|
|
587
|
+
"domain-rules": "domain_rules",
|
|
588
|
+
"domain": "domain_rules",
|
|
589
|
+
"business-rules": "domain_rules",
|
|
590
|
+
// Layer 2: Architecture Flow (Issue #17)
|
|
591
|
+
"arch_flow": "arch_flow",
|
|
592
|
+
"arch-flow": "arch_flow",
|
|
593
|
+
"architecture": "arch_flow",
|
|
594
|
+
"flow-map": "arch_flow",
|
|
595
|
+
// Layer 3: Gotchas (Issue #17 / #21)
|
|
596
|
+
"gotcha": "gotcha",
|
|
597
|
+
"gotchas": "gotcha",
|
|
598
|
+
"known-gotchas": "gotcha",
|
|
599
|
+
"legacy": "gotcha",
|
|
600
|
+
"quirk": "gotcha",
|
|
601
|
+
// Layers summary
|
|
602
|
+
"layers": "layers",
|
|
603
|
+
"3-layer": "layers",
|
|
604
|
+
"memory-layers": "layers"
|
|
522
605
|
};
|
|
523
606
|
async function handleMemory(params) {
|
|
524
607
|
const rawAction = params.action || "session";
|
|
@@ -548,6 +631,14 @@ async function handleMemory(params) {
|
|
|
548
631
|
return handleBenchmark(params);
|
|
549
632
|
case "decision_log":
|
|
550
633
|
return handleDecisionLog(params);
|
|
634
|
+
case "domain_rules":
|
|
635
|
+
return handleLayerAction("domain_rules", params);
|
|
636
|
+
case "arch_flow":
|
|
637
|
+
return handleLayerAction("arch_flow", params);
|
|
638
|
+
case "gotcha":
|
|
639
|
+
return handleGotchaAction(params);
|
|
640
|
+
case "layers":
|
|
641
|
+
return handleLayersSummary(params);
|
|
551
642
|
default:
|
|
552
643
|
return `Unknown action "${action}".`;
|
|
553
644
|
}
|
|
@@ -558,7 +649,7 @@ async function handleDecision(params) {
|
|
|
558
649
|
case "template":
|
|
559
650
|
return formatDecisionTemplate();
|
|
560
651
|
case "suggest": {
|
|
561
|
-
const { shouldRecordDecision } = await import("./kumaMemory-
|
|
652
|
+
const { shouldRecordDecision } = await import("./kumaMemory-7TCUDVZX.js");
|
|
562
653
|
const check = shouldRecordDecision();
|
|
563
654
|
return check.worth ? `\u{1F4A1} Decision suggested: "${check.title}"
|
|
564
655
|
Use kuma_memory({ action: 'decision', title: '...', context: '...', rationale: '...', outcome: '...' }) to record.` : "\u2705 No decision needed at this time.";
|
|
@@ -636,8 +727,17 @@ async function handleSearch(params) {
|
|
|
636
727
|
if (!query) return "\u26A0\uFE0F query or scope parameter required.";
|
|
637
728
|
const limit = params.limit || 20;
|
|
638
729
|
const memResults = sessionMemory.searchMemory(query, limit);
|
|
639
|
-
const { searchGraph: searchGraph2 } = await import("./kumaGraph-
|
|
730
|
+
const { searchGraph: searchGraph2 } = await import("./kumaGraph-AWP743TJ.js");
|
|
640
731
|
const graphResults = await searchGraph2(query, Math.min(limit, 10));
|
|
732
|
+
let hybridResults = "";
|
|
733
|
+
try {
|
|
734
|
+
const { hybridSearch, formatHybridResults } = await import("./kumaSearch-QGB4QVXS.js");
|
|
735
|
+
const semanticResults = await hybridSearch(query, 8);
|
|
736
|
+
if (semanticResults.length > 0) {
|
|
737
|
+
hybridResults = "\n" + formatHybridResults(query, semanticResults);
|
|
738
|
+
}
|
|
739
|
+
} catch {
|
|
740
|
+
}
|
|
641
741
|
const lines = [`\u{1F50D} **Search Results** \u2014 "${query}"
|
|
642
742
|
\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
|
|
643
743
|
`];
|
|
@@ -647,6 +747,10 @@ async function handleSearch(params) {
|
|
|
647
747
|
lines.push("");
|
|
648
748
|
}
|
|
649
749
|
lines.push("**Knowledge Graph:**\n" + graphResults);
|
|
750
|
+
if (hybridResults) {
|
|
751
|
+
lines.push("");
|
|
752
|
+
lines.push(hybridResults);
|
|
753
|
+
}
|
|
650
754
|
return lines.join("\n");
|
|
651
755
|
}
|
|
652
756
|
async function handleChanges2(params) {
|
|
@@ -711,7 +815,7 @@ async function handleDecisionLog(params) {
|
|
|
711
815
|
}
|
|
712
816
|
async function handleMine(params) {
|
|
713
817
|
sessionMemory.recordToolCall("kuma_memory_mine", { scope: params.scope });
|
|
714
|
-
const { mineHistoricalDecisions } = await import("./kumaMiner-
|
|
818
|
+
const { mineHistoricalDecisions } = await import("./kumaMiner-FFXSRHAO.js");
|
|
715
819
|
return await mineHistoricalDecisions({
|
|
716
820
|
scope: params.scope,
|
|
717
821
|
since: typeof params.since === "string" ? params.since : void 0,
|
|
@@ -719,6 +823,34 @@ async function handleMine(params) {
|
|
|
719
823
|
limit: params.limit
|
|
720
824
|
});
|
|
721
825
|
}
|
|
826
|
+
async function handleLayerAction(layer, params) {
|
|
827
|
+
const { readLayer, writeLayer } = await import("./domainRules-LDZPOIGZ.js");
|
|
828
|
+
if (params.content) {
|
|
829
|
+
return writeLayer(layer, params.content);
|
|
830
|
+
}
|
|
831
|
+
return readLayer(layer);
|
|
832
|
+
}
|
|
833
|
+
async function handleGotchaAction(params) {
|
|
834
|
+
if (params.content && params.scope) {
|
|
835
|
+
const { addGotcha } = await import("./kumaGotchas-DU5H6N7X.js");
|
|
836
|
+
return await addGotcha({
|
|
837
|
+
filePath: params.scope,
|
|
838
|
+
description: params.content,
|
|
839
|
+
severity: params.status || "medium",
|
|
840
|
+
workaround: params.description
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
const { listGotchas, syncGotchasToDb } = await import("./kumaGotchas-DU5H6N7X.js");
|
|
844
|
+
await syncGotchasToDb();
|
|
845
|
+
return await listGotchas({
|
|
846
|
+
filePath: params.scope,
|
|
847
|
+
severity: params.status
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
async function handleLayersSummary(_params) {
|
|
851
|
+
const { getLayersSummary } = await import("./domainRules-LDZPOIGZ.js");
|
|
852
|
+
return getLayersSummary();
|
|
853
|
+
}
|
|
722
854
|
|
|
723
855
|
// src/engine/kumaLock.ts
|
|
724
856
|
import fs3 from "fs";
|
|
@@ -827,151 +959,6 @@ function cleanStaleLocks() {
|
|
|
827
959
|
return count > 0 ? `\u{1F9F9} Cleaned ${count} stale lock(s).` : "\u2705 No stale locks found.";
|
|
828
960
|
}
|
|
829
961
|
|
|
830
|
-
// src/engine/safetyAudit.ts
|
|
831
|
-
var SCHEMA_SQL = `
|
|
832
|
-
CREATE TABLE IF NOT EXISTS safety_audit (
|
|
833
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
834
|
-
timestamp INTEGER NOT NULL,
|
|
835
|
-
tool_name TEXT NOT NULL,
|
|
836
|
-
action TEXT NOT NULL,
|
|
837
|
-
file_path TEXT,
|
|
838
|
-
risk_level TEXT NOT NULL DEFAULT 'low',
|
|
839
|
-
policy_violations INTEGER DEFAULT 0,
|
|
840
|
-
allowed INTEGER NOT NULL DEFAULT 1,
|
|
841
|
-
duration_ms INTEGER DEFAULT 0,
|
|
842
|
-
metadata TEXT DEFAULT '{}'
|
|
843
|
-
);
|
|
844
|
-
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON safety_audit(timestamp);
|
|
845
|
-
CREATE INDEX IF NOT EXISTS idx_audit_tool ON safety_audit(tool_name);
|
|
846
|
-
CREATE INDEX IF NOT EXISTS idx_audit_risk ON safety_audit(risk_level);
|
|
847
|
-
CREATE INDEX IF NOT EXISTS idx_audit_allowed ON safety_audit(allowed);
|
|
848
|
-
`;
|
|
849
|
-
var schemaEnsured = false;
|
|
850
|
-
async function ensureSchema() {
|
|
851
|
-
if (schemaEnsured) return;
|
|
852
|
-
try {
|
|
853
|
-
const db = await getDb();
|
|
854
|
-
db.exec(SCHEMA_SQL);
|
|
855
|
-
saveDb();
|
|
856
|
-
schemaEnsured = true;
|
|
857
|
-
} catch (err) {
|
|
858
|
-
console.error(`[SafetyAudit] Failed to ensure schema: ${err}`);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
async function recordAudit(entry) {
|
|
862
|
-
try {
|
|
863
|
-
await ensureSchema();
|
|
864
|
-
const db = await getDb();
|
|
865
|
-
db.run(`
|
|
866
|
-
INSERT INTO safety_audit (timestamp, tool_name, action, file_path, risk_level, policy_violations, allowed, duration_ms, metadata)
|
|
867
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
868
|
-
`, [
|
|
869
|
-
entry.timestamp,
|
|
870
|
-
entry.toolName,
|
|
871
|
-
entry.action,
|
|
872
|
-
entry.filePath || null,
|
|
873
|
-
entry.riskLevel,
|
|
874
|
-
entry.policyViolations,
|
|
875
|
-
entry.allowed ? 1 : 0,
|
|
876
|
-
entry.durationMs,
|
|
877
|
-
JSON.stringify(entry.metadata || {})
|
|
878
|
-
]);
|
|
879
|
-
saveDb();
|
|
880
|
-
} catch (err) {
|
|
881
|
-
console.error(`[SafetyAudit] Failed to record audit: ${err}`);
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
async function queryAudit(params) {
|
|
885
|
-
try {
|
|
886
|
-
await ensureSchema();
|
|
887
|
-
const db = await getDb();
|
|
888
|
-
const { toolName, riskLevel, allowed, limit = 20, since } = params;
|
|
889
|
-
let sql = "SELECT * FROM safety_audit WHERE 1=1";
|
|
890
|
-
const bindParams = [];
|
|
891
|
-
if (toolName) {
|
|
892
|
-
sql += " AND tool_name = ?";
|
|
893
|
-
bindParams.push(toolName);
|
|
894
|
-
}
|
|
895
|
-
if (riskLevel) {
|
|
896
|
-
sql += " AND risk_level = ?";
|
|
897
|
-
bindParams.push(riskLevel);
|
|
898
|
-
}
|
|
899
|
-
if (allowed !== void 0) {
|
|
900
|
-
sql += " AND allowed = ?";
|
|
901
|
-
bindParams.push(allowed ? 1 : 0);
|
|
902
|
-
}
|
|
903
|
-
if (since) {
|
|
904
|
-
sql += " AND timestamp >= ?";
|
|
905
|
-
bindParams.push(since);
|
|
906
|
-
}
|
|
907
|
-
sql += " ORDER BY timestamp DESC LIMIT ?";
|
|
908
|
-
bindParams.push(limit);
|
|
909
|
-
const stmt = db.prepare(sql);
|
|
910
|
-
stmt.bind(bindParams);
|
|
911
|
-
const results = [];
|
|
912
|
-
while (stmt.step()) {
|
|
913
|
-
results.push(stmt.getAsObject());
|
|
914
|
-
}
|
|
915
|
-
stmt.free();
|
|
916
|
-
if (results.length === 0) {
|
|
917
|
-
return "\u{1F4CB} **Safety Audit** \u2014 No records found for the given filters.";
|
|
918
|
-
}
|
|
919
|
-
const lines = [
|
|
920
|
-
`\u{1F4CB} **Safety Audit** \u2014 ${results.length} record(s)`,
|
|
921
|
-
`\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`,
|
|
922
|
-
""
|
|
923
|
-
];
|
|
924
|
-
for (const r of results) {
|
|
925
|
-
const riskIcon = r.risk_level === "critical" ? "\u{1F534}" : r.risk_level === "high" ? "\u{1F7E0}" : r.risk_level === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
926
|
-
const allowIcon = r.allowed ? "\u2705" : "\u26D4";
|
|
927
|
-
const time = new Date(r.timestamp * 1e3).toLocaleTimeString();
|
|
928
|
-
lines.push(`${allowIcon} ${riskIcon} **${r.tool_name}** \u2014 ${r.action}`);
|
|
929
|
-
if (r.file_path) lines.push(` \u{1F4CD} ${r.file_path}`);
|
|
930
|
-
lines.push(` \u{1F550} ${time} | ${r.duration_ms}ms | ${r.policy_violations} policy violation(s)`);
|
|
931
|
-
lines.push("");
|
|
932
|
-
}
|
|
933
|
-
return lines.join("\n");
|
|
934
|
-
} catch (err) {
|
|
935
|
-
return `Error querying audit: ${err}`;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
async function auditStats() {
|
|
939
|
-
try {
|
|
940
|
-
await ensureSchema();
|
|
941
|
-
const db = await getDb();
|
|
942
|
-
const total = db.exec("SELECT COUNT(*) as c FROM safety_audit")[0]?.values[0][0] ?? 0;
|
|
943
|
-
const blocked = db.exec("SELECT COUNT(*) as c FROM safety_audit WHERE allowed = 0")[0]?.values[0][0] ?? 0;
|
|
944
|
-
const critical = db.exec("SELECT COUNT(*) as c FROM safety_audit WHERE risk_level IN ('high','critical')")[0]?.values[0][0] ?? 0;
|
|
945
|
-
let topBlocked = [];
|
|
946
|
-
try {
|
|
947
|
-
const stmt = db.prepare("SELECT tool_name, COUNT(*) as cnt FROM safety_audit WHERE allowed = 0 GROUP BY tool_name ORDER BY cnt DESC LIMIT 5");
|
|
948
|
-
while (stmt.step()) {
|
|
949
|
-
topBlocked.push(stmt.getAsObject());
|
|
950
|
-
}
|
|
951
|
-
stmt.free();
|
|
952
|
-
} catch {
|
|
953
|
-
}
|
|
954
|
-
const lines = [
|
|
955
|
-
`\u{1F4CA} **Safety Audit Stats**`,
|
|
956
|
-
`\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`,
|
|
957
|
-
"",
|
|
958
|
-
`\u{1F4DD} Total operations: ${total}`,
|
|
959
|
-
`\u26D4 Blocked: ${blocked} (${total > 0 ? (blocked / total * 100).toFixed(1) : 0}%)`,
|
|
960
|
-
`\u{1F534} High/Critical risk: ${critical}`,
|
|
961
|
-
""
|
|
962
|
-
];
|
|
963
|
-
if (topBlocked.length > 0) {
|
|
964
|
-
lines.push("**Most blocked tools:**");
|
|
965
|
-
for (const t of topBlocked) {
|
|
966
|
-
lines.push(` \u2022 ${t.tool_name}: ${t.cnt}x blocked`);
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
return lines.join("\n");
|
|
970
|
-
} catch (err) {
|
|
971
|
-
return `Error getting audit stats: ${err}`;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
962
|
// src/tools/kumaPolicy.ts
|
|
976
963
|
import fs4 from "fs";
|
|
977
964
|
import path4 from "path";
|
|
@@ -1805,6 +1792,11 @@ async function handleSafety(params) {
|
|
|
1805
1792
|
return handleGitignore(params);
|
|
1806
1793
|
case "clean":
|
|
1807
1794
|
return handleClean(params);
|
|
1795
|
+
case "policy":
|
|
1796
|
+
return handlePolicy(params);
|
|
1797
|
+
case "ast":
|
|
1798
|
+
case "validate":
|
|
1799
|
+
return handleAstValidation(params);
|
|
1808
1800
|
default:
|
|
1809
1801
|
return `Unknown action "${action}".`;
|
|
1810
1802
|
}
|
|
@@ -1848,7 +1840,7 @@ async function handleLock(params) {
|
|
|
1848
1840
|
}
|
|
1849
1841
|
async function handleHealth2(_params) {
|
|
1850
1842
|
try {
|
|
1851
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
1843
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-GR5N55CU.js");
|
|
1852
1844
|
const score = await computeSafetyScore();
|
|
1853
1845
|
await saveHealthSnapshot(score.score, score.risk, JSON.stringify(score.checks), score.summary);
|
|
1854
1846
|
return formatSafetyScore(score);
|
|
@@ -1952,6 +1944,52 @@ async function handleClean(_params) {
|
|
|
1952
1944
|
return `${result}
|
|
1953
1945
|
\u{1F4A1} Drift warnings have been reset. Any temporary debug artifacts are now cleared.`;
|
|
1954
1946
|
}
|
|
1947
|
+
async function handlePolicy(params) {
|
|
1948
|
+
sessionMemory.recordToolCall("kuma_safety_policy", {});
|
|
1949
|
+
if (params.command) {
|
|
1950
|
+
const { evaluateCommand, evaluateFilePath } = await import("./kumaPolicyEngine-S2PXN3C7.js");
|
|
1951
|
+
const commandVerdict = evaluateCommand(params.command);
|
|
1952
|
+
const lines = [];
|
|
1953
|
+
lines.push(`\u{1F4DC} **Policy Check**: ${commandVerdict.allowed ? "\u2705 Allowed" : "\u26D4 Blocked"}`);
|
|
1954
|
+
lines.push(`\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`);
|
|
1955
|
+
lines.push("");
|
|
1956
|
+
lines.push(`\u{1F4BB} Command: \`${params.command}\``);
|
|
1957
|
+
lines.push(`\u{1F4CB} Result: ${commandVerdict.message}`);
|
|
1958
|
+
if (commandVerdict.blockedBy) {
|
|
1959
|
+
lines.push("");
|
|
1960
|
+
lines.push(`\u{1F534} **Blocked by rule**: ${commandVerdict.blockedBy.description}`);
|
|
1961
|
+
if (commandVerdict.requiresOverride) {
|
|
1962
|
+
lines.push(`\u{1F511} Use kuma_safety({ action: 'override', toolName: 'policy', reason: '...' }) to bypass.`);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
for (const w of commandVerdict.warnings) {
|
|
1966
|
+
lines.push(`\u{1F7E1} **Warning**: ${w.description}`);
|
|
1967
|
+
}
|
|
1968
|
+
if (params.filePath) {
|
|
1969
|
+
const fileVerdict = evaluateFilePath(params.filePath);
|
|
1970
|
+
if (!fileVerdict.allowed) {
|
|
1971
|
+
lines.push("");
|
|
1972
|
+
lines.push(fileVerdict.message);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
return lines.join("\n");
|
|
1976
|
+
}
|
|
1977
|
+
const { formatPolicyStatus } = await import("./kumaPolicyEngine-S2PXN3C7.js");
|
|
1978
|
+
return formatPolicyStatus();
|
|
1979
|
+
}
|
|
1980
|
+
async function handleAstValidation(params) {
|
|
1981
|
+
sessionMemory.recordToolCall("kuma_safety_ast", { scope: params.scope });
|
|
1982
|
+
const { validateCodeContent, validateFile, formatValidationFindings } = await import("./kumaAstValidator-CNM7FHYA.js");
|
|
1983
|
+
if (params.command) {
|
|
1984
|
+
const findings = validateCodeContent(params.command, params.scope);
|
|
1985
|
+
return formatValidationFindings(findings, params.scope);
|
|
1986
|
+
}
|
|
1987
|
+
if (params.scope) {
|
|
1988
|
+
const findings = validateFile(params.scope);
|
|
1989
|
+
return formatValidationFindings(findings, params.scope);
|
|
1990
|
+
}
|
|
1991
|
+
return "\u26A0\uFE0F Provide a scope (file path) or command (code content) to validate.";
|
|
1992
|
+
}
|
|
1955
1993
|
var _lastVerifyCall = 0;
|
|
1956
1994
|
var VERIFY_COOLDOWN_MS = 3e4;
|
|
1957
1995
|
async function handleVerify(params) {
|
|
@@ -1962,7 +2000,7 @@ async function handleVerify(params) {
|
|
|
1962
2000
|
}
|
|
1963
2001
|
_lastVerifyCall = now;
|
|
1964
2002
|
sessionMemory.recordToolCall("kuma_safety_verify", { scope: params.scope || params.filePath });
|
|
1965
|
-
const { runAutoVerification } = await import("./kumaVerifier-
|
|
2003
|
+
const { runAutoVerification } = await import("./kumaVerifier-ARSGPZAM.js");
|
|
1966
2004
|
return await runAutoVerification({
|
|
1967
2005
|
scope: params.scope || params.filePath,
|
|
1968
2006
|
force: params.force,
|
|
@@ -1976,7 +2014,7 @@ function registerAllTools(server) {
|
|
|
1976
2014
|
"kuma_context",
|
|
1977
2015
|
"**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.",
|
|
1978
2016
|
{
|
|
1979
|
-
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"),
|
|
2017
|
+
action: z.enum(["init", "research", "impact", "navigate", "changes", "health", "rollback", "researches", "sync", "visualize", "digest", "drift"]).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, digest=ultra-compact <500 token project briefing (Issue #18), drift=detect memory staleness & code drift (Issue #20), health=project health score"),
|
|
1980
2018
|
scope: z.string().optional().describe("Research scope for research action (e.g. 'auth', 'database', 'api')"),
|
|
1981
2019
|
target: z.string().optional().describe("Target symbol/file for impact/navigate/changes"),
|
|
1982
2020
|
goal: z.string().optional().describe("Current goal (for init/health)"),
|
|
@@ -2003,7 +2041,7 @@ function registerAllTools(server) {
|
|
|
2003
2041
|
"kuma_memory",
|
|
2004
2042
|
"Record and retrieve project knowledge. Actions: decision (ADR-style record/template/suggest), mine (mine historical decisions from git log & code comments), research_save (save research), session (session summary), heal (graph repair), search (search all), changes (change log), todo (persistent todo CRUD), context (inject context notes), benchmark (capture/diff metrics), decision_log (living document with status tracking).",
|
|
2005
2043
|
{
|
|
2006
|
-
action: z.enum(["decision", "mine", "research_save", "session", "heal", "search", "changes", "todo", "context", "benchmark", "decision_log"]).describe("Memory action: decision=ADR, mine=mine git log & comments, research_save=save, session=summary, heal=repair, search=search, changes=log, todo=manage todos, context=inject notes, benchmark=capture/diff, decision_log=manage decisions"),
|
|
2044
|
+
action: z.enum(["decision", "mine", "research_save", "session", "heal", "search", "changes", "todo", "context", "benchmark", "decision_log", "domain_rules", "arch_flow", "gotcha", "layers"]).describe("Memory action: decision=ADR, mine=mine git log & comments, research_save=save, session=summary, heal=repair, search=search, changes=log, todo=manage todos, context=inject notes, benchmark=capture/diff, decision_log=manage decisions, domain_rules=Layer 1 business rules (Issue #17), arch_flow=Layer 2 architecture flow (Issue #17), gotcha=Layer 3 known gotchas (Issue #17/#21), layers=all 3 layers summary"),
|
|
2007
2045
|
scope: z.string().optional().describe("Scope for research_save/search/todo/context/mine"),
|
|
2008
2046
|
query: z.string().optional().describe("Search query for search action"),
|
|
2009
2047
|
content: z.string().optional().describe("Content/notes for research_save / context"),
|
|
@@ -2067,7 +2105,7 @@ function registerAllTools(server) {
|
|
|
2067
2105
|
"kuma_safety",
|
|
2068
2106
|
"Safety checks, policy enforcement, security scanning, integrated auto-verification, and project hygiene. Actions: guard (anti-pattern detection), verify (auto-run scoped tests), check (pre-exec safety), audit (query trail), lock (multi-agent), health (score), security (scan for leaks), gc (garbage collection), doctor (health check), portability (path check), gitignore (auto-config), override (bypass).",
|
|
2069
2107
|
{
|
|
2070
|
-
action: z.enum(["guard", "verify", "check", "audit", "lock", "health", "override", "security", "gc", "doctor", "portability", "gitignore", "clean"]).describe("Safety action: guard=anti-patterns, verify=auto-run scoped tests, check=pre-exec safety, audit=query trail, lock=multi-agent, health=score, security=scan leaks, gc=garbage collect, doctor=health check, portability=paths, gitignore=config, clean=purge scratch dir & drift, override=bypass"),
|
|
2108
|
+
action: z.enum(["guard", "verify", "check", "audit", "lock", "health", "override", "security", "gc", "doctor", "portability", "gitignore", "clean", "policy", "ast", "validate"]).describe("Safety action: guard=anti-patterns, verify=auto-run scoped tests, check=pre-exec safety, audit=query trail, lock=multi-agent, health=score, security=scan leaks, gc=garbage collect, doctor=health check, portability=paths, gitignore=config, clean=purge scratch dir & drift, policy=Policy-as-Code engine (Issue #24), ast/validate=AST-based code validation (Issue #22), override=bypass"),
|
|
2071
2109
|
// Verify params
|
|
2072
2110
|
scope: z.string().optional().describe("Scope for verify or context (e.g. 'auth', file path)"),
|
|
2073
2111
|
// Guard params
|
|
@@ -2161,7 +2199,7 @@ async function main() {
|
|
|
2161
2199
|
console.error("");
|
|
2162
2200
|
let killedCount = 0;
|
|
2163
2201
|
try {
|
|
2164
|
-
const { getRunningVerificationPid } = await import("./kumaVerifier-
|
|
2202
|
+
const { getRunningVerificationPid } = await import("./kumaVerifier-ARSGPZAM.js");
|
|
2165
2203
|
const pid = getRunningVerificationPid();
|
|
2166
2204
|
if (pid) {
|
|
2167
2205
|
try {
|
|
@@ -2253,6 +2291,9 @@ async function main() {
|
|
|
2253
2291
|
console.error("");
|
|
2254
2292
|
process.exit(0);
|
|
2255
2293
|
}
|
|
2294
|
+
if (args[0] === "--hook") {
|
|
2295
|
+
process.exit(0);
|
|
2296
|
+
}
|
|
2256
2297
|
if (args[0] === "init") {
|
|
2257
2298
|
const flags = args.slice(1);
|
|
2258
2299
|
if (flags.includes("--help") || flags.includes("-h")) {
|
|
@@ -2400,7 +2441,7 @@ async function main() {
|
|
|
2400
2441
|
console.error(`[${SERVER_NAME}] \u2705 Restored session (${sessionInfo.toolCallCount} previous tool calls)`);
|
|
2401
2442
|
}
|
|
2402
2443
|
try {
|
|
2403
|
-
const { buildFromSessionMemory } = await import("./kumaGraph-
|
|
2444
|
+
const { buildFromSessionMemory } = await import("./kumaGraph-AWP743TJ.js");
|
|
2404
2445
|
const edgeCount = await buildFromSessionMemory();
|
|
2405
2446
|
if (edgeCount > 0) {
|
|
2406
2447
|
console.error(`[${SERVER_NAME}] \u2705 Graph auto-populated with ${edgeCount} entries from session memory`);
|
|
@@ -2420,16 +2461,54 @@ async function main() {
|
|
|
2420
2461
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Scratch directory setup: ${err}`);
|
|
2421
2462
|
}
|
|
2422
2463
|
try {
|
|
2423
|
-
const { getDb: getDb2, saveDb
|
|
2464
|
+
const { getDb: getDb2, saveDb } = await import("./kumaDb-6D53ERFP.js");
|
|
2424
2465
|
const db = await getDb2();
|
|
2425
2466
|
db.run(
|
|
2426
2467
|
`INSERT INTO sessions (started_at, goal, tool_calls) VALUES (?, ?, ?)`,
|
|
2427
2468
|
[Math.floor(Date.now() / 1e3), sessionMemory.getSummary().currentGoal || "Session start", sessionInfo.toolCallCount]
|
|
2428
2469
|
);
|
|
2429
|
-
|
|
2470
|
+
saveDb(db);
|
|
2430
2471
|
} catch (err) {
|
|
2431
2472
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Session DB record: ${err}`);
|
|
2432
2473
|
}
|
|
2474
|
+
try {
|
|
2475
|
+
const fs7 = await import("fs");
|
|
2476
|
+
const path7 = await import("path");
|
|
2477
|
+
const hooksDir = path7.resolve(process.cwd(), ".kuma", "hooks");
|
|
2478
|
+
if (!fs7.existsSync(hooksDir)) {
|
|
2479
|
+
fs7.mkdirSync(hooksDir, { recursive: true });
|
|
2480
|
+
const hookContent = `#!/bin/bash
|
|
2481
|
+
# Kuma auto-capture hook (post-commit)
|
|
2482
|
+
# Auto-updates knowledge graph after git commits
|
|
2483
|
+
if command -v npx &> /dev/null; then
|
|
2484
|
+
npx -y @plumpslabs/kuma --hook post-commit 2>/dev/null || true
|
|
2485
|
+
fi
|
|
2486
|
+
`;
|
|
2487
|
+
const gitHooksDir = path7.resolve(process.cwd(), ".git", "hooks");
|
|
2488
|
+
if (fs7.existsSync(gitHooksDir)) {
|
|
2489
|
+
const postCommitPath = path7.join(gitHooksDir, "post-commit");
|
|
2490
|
+
if (!fs7.existsSync(postCommitPath)) {
|
|
2491
|
+
fs7.writeFileSync(postCommitPath, hookContent, "utf-8");
|
|
2492
|
+
try {
|
|
2493
|
+
fs7.chmodSync(postCommitPath, 493);
|
|
2494
|
+
} catch {
|
|
2495
|
+
}
|
|
2496
|
+
console.error(`[${SERVER_NAME}] \u2705 Created .git/hooks/post-commit for auto-capture`);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
} catch (err) {
|
|
2501
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Auto-capture hook setup: ${err}`);
|
|
2502
|
+
}
|
|
2503
|
+
try {
|
|
2504
|
+
const { buildSearchVectors } = await import("./kumaSearch-QGB4QVXS.js");
|
|
2505
|
+
const vectors = await buildSearchVectors();
|
|
2506
|
+
if (vectors.length > 0) {
|
|
2507
|
+
console.error(`[${SERVER_NAME}] \u2705 Search vector cache pre-warmed (${vectors.length} documents)`);
|
|
2508
|
+
}
|
|
2509
|
+
} catch (err) {
|
|
2510
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Search vector cache: ${err}`);
|
|
2511
|
+
}
|
|
2433
2512
|
console.error(`[${SERVER_NAME}] \u2705 Cold start bootstrap complete`);
|
|
2434
2513
|
} catch (err) {
|
|
2435
2514
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Cold start bootstrap error: ${err}`);
|