@plumpslabs/kuma 2.3.1 → 2.3.2
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 +2 -0
- package/dist/{chunk-2IIDVJPW.js → chunk-5N6KXACT.js} +45 -1
- package/dist/{chunk-4CKTTORL.js → chunk-EIK7T746.js} +2 -2
- package/dist/{chunk-ZDQVSBFB.js → chunk-EQ2CE4UC.js} +2 -2
- package/dist/{chunk-WG47POMW.js → chunk-JIL533AM.js} +1 -1
- package/dist/{chunk-5WSPGLXS.js → chunk-L5WU2HTN.js} +2 -0
- package/dist/{chunk-6W7YV4AF.js → chunk-SU7BTOND.js} +2 -2
- package/dist/index.js +60 -31
- package/dist/{init-3MBHSTWD.js → init-AJAESUZZ.js} +1 -1
- package/dist/{kumaDb-PA7XVERC.js → kumaDb-RC2EO3OB.js} +5 -1
- package/dist/{kumaGraph-VUTLTOFY.js → kumaGraph-5IZ4FDZA.js} +2 -2
- package/dist/{kumaMemory-YEIEXGNW.js → kumaMemory-ME4XWD5Z.js} +2 -2
- package/dist/kumaMiner-GFEGUWGH.js +142 -0
- package/dist/kumaVerifier-VYW5WH6V.js +108 -0
- package/dist/{safetyScore-OQFYLLP7.js → safetyScore-QX2LBJ7H.js} +30 -6
- package/dist/{sessionMemory-JQT5GEWO.js → sessionMemory-LI4FIAMF.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,6 +122,7 @@ Kuma consolidates everything into **3 coarse-grained tools**. Each action trigge
|
|
|
122
122
|
| Action | Use case |
|
|
123
123
|
|--------|----------|
|
|
124
124
|
| `decision` | ADR-style recording: template, suggest, or record. |
|
|
125
|
+
| `mine` | Mine historical decisions from git log & inline code comments. |
|
|
125
126
|
| `research_save` | Persist research to graph + `.kuma/research/<scope>.json`. |
|
|
126
127
|
| `session` | "What happened this session?" — files, failures, progress. |
|
|
127
128
|
| `heal` | Self-heal knowledge graph — stale detection, git repair. |
|
|
@@ -133,6 +134,7 @@ Kuma consolidates everything into **3 coarse-grained tools**. Each action trigge
|
|
|
133
134
|
| Action | Use case |
|
|
134
135
|
|--------|----------|
|
|
135
136
|
| `guard` | Anti-pattern, drift, tool-loop, and failure checks. |
|
|
137
|
+
| `verify` | Integrated auto-verification — auto-detect runner & execute scoped tests. |
|
|
136
138
|
| `check` | Pre-execution safety: policy, path, lock, risk level. |
|
|
137
139
|
| `audit` | Query audit trail + stats + override log. |
|
|
138
140
|
| `lock` | Multi-agent file locking. |
|
|
@@ -239,6 +239,16 @@ function createSchema(db) {
|
|
|
239
239
|
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
240
240
|
updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
|
|
241
241
|
)`);
|
|
242
|
+
db.run(`CREATE TABLE IF NOT EXISTS verifications (
|
|
243
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
244
|
+
scope TEXT NOT NULL,
|
|
245
|
+
runner TEXT NOT NULL,
|
|
246
|
+
test_command TEXT NOT NULL,
|
|
247
|
+
passed INTEGER NOT NULL,
|
|
248
|
+
output TEXT,
|
|
249
|
+
duration_ms INTEGER,
|
|
250
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
|
|
251
|
+
)`);
|
|
242
252
|
db.run(`CREATE TABLE IF NOT EXISTS portability_entries (
|
|
243
253
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
244
254
|
entry_type TEXT NOT NULL,
|
|
@@ -804,6 +814,38 @@ async function saveHealthSnapshot(score, riskLevel, checks, summary) {
|
|
|
804
814
|
console.error(`[KumaDB] Failed to save health snapshot: ${err}`);
|
|
805
815
|
}
|
|
806
816
|
}
|
|
817
|
+
async function saveVerification(scope, runner, command, passed, output, durationMs = 0) {
|
|
818
|
+
try {
|
|
819
|
+
const db = await getDb();
|
|
820
|
+
db.run(
|
|
821
|
+
`INSERT INTO verifications (scope, runner, test_command, passed, output, duration_ms) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
822
|
+
[scope, runner, command, passed ? 1 : 0, output, durationMs]
|
|
823
|
+
);
|
|
824
|
+
saveDb();
|
|
825
|
+
} catch (err) {
|
|
826
|
+
console.error(`[KumaDB] Failed to save verification: ${err}`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
async function getLatestVerifications(limit = 10) {
|
|
830
|
+
try {
|
|
831
|
+
const db = await getDb();
|
|
832
|
+
const res = db.exec(`SELECT id, scope, runner, test_command, passed, output, duration_ms, created_at FROM verifications ORDER BY created_at DESC LIMIT ${limit}`);
|
|
833
|
+
if (!res[0] || !res[0].values) return [];
|
|
834
|
+
return res[0].values.map((row) => ({
|
|
835
|
+
id: row[0],
|
|
836
|
+
scope: row[1],
|
|
837
|
+
runner: row[2],
|
|
838
|
+
test_command: row[3],
|
|
839
|
+
passed: Boolean(row[4]),
|
|
840
|
+
output: row[5],
|
|
841
|
+
duration_ms: row[6],
|
|
842
|
+
created_at: row[7]
|
|
843
|
+
}));
|
|
844
|
+
} catch (err) {
|
|
845
|
+
console.error(`[KumaDB] Failed to get verifications: ${err}`);
|
|
846
|
+
return [];
|
|
847
|
+
}
|
|
848
|
+
}
|
|
807
849
|
|
|
808
850
|
export {
|
|
809
851
|
getDb,
|
|
@@ -832,5 +874,7 @@ export {
|
|
|
832
874
|
runDoctor,
|
|
833
875
|
checkPortability,
|
|
834
876
|
ensureGitignore,
|
|
835
|
-
saveHealthSnapshot
|
|
877
|
+
saveHealthSnapshot,
|
|
878
|
+
saveVerification,
|
|
879
|
+
getLatestVerifications
|
|
836
880
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sessionMemory
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SU7BTOND.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-5IZ4FDZA.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,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDb,
|
|
3
3
|
saveDb
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5N6KXACT.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-LI4FIAMF.js");
|
|
462
462
|
const toolCalls = sessionMemory.getToolCallHistory(50);
|
|
463
463
|
let edgeCount = 0;
|
|
464
464
|
for (const call of toolCalls) {
|
|
@@ -270,6 +270,7 @@ function generateInitMdContent() {
|
|
|
270
270
|
"",
|
|
271
271
|
'- `kuma_memory({ action: "research_save", scope: "...", ... })` \u2014 Save research results',
|
|
272
272
|
'- `kuma_memory({ action: "decision", decisionAction: "record", ... })` \u2014 ADR-style decision',
|
|
273
|
+
'- **`kuma_memory({ action: "mine", scope: "..." })` \u2014 Mine historical decisions from git log & comments**',
|
|
273
274
|
'- `kuma_memory({ action: "session" })` \u2014 Session summary',
|
|
274
275
|
'- `kuma_memory({ action: "heal" })` \u2014 Self-heal knowledge graph',
|
|
275
276
|
'- `kuma_memory({ action: "search", query: "..." })` \u2014 Search memories + graph',
|
|
@@ -278,6 +279,7 @@ function generateInitMdContent() {
|
|
|
278
279
|
"### \u{1F6E1}\uFE0F kuma_safety \u2014 Safety & Policy",
|
|
279
280
|
"",
|
|
280
281
|
'- `kuma_safety({ action: "guard", guardGoal: "..." })` \u2014 Anti-pattern, drift, loop detection',
|
|
282
|
+
'- **`kuma_safety({ action: "verify", scope: "..." })` \u2014 Auto-run scoped tests & verify correctness**',
|
|
281
283
|
'- `kuma_safety({ action: "check", ... })` \u2014 Pre-execution safety check',
|
|
282
284
|
'- `kuma_safety({ action: "audit" })` \u2014 Query safety audit trail',
|
|
283
285
|
'- `kuma_safety({ action: "lock", lockAction: "acquire", ... })` \u2014 Multi-agent file lock',
|
|
@@ -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-RC2EO3OB.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, ?)`,
|
|
@@ -597,7 +597,7 @@ No unresolved issues.`;
|
|
|
597
597
|
this.addModifiedFile(filePath);
|
|
598
598
|
}
|
|
599
599
|
try {
|
|
600
|
-
const { recordChange } = await import("./kumaDb-
|
|
600
|
+
const { recordChange } = await import("./kumaDb-RC2EO3OB.js");
|
|
601
601
|
const gitHash = this.getGitHead();
|
|
602
602
|
await recordChange({ filePath, changeType, symbol, gitCommitHash: gitHash || void 0 });
|
|
603
603
|
} catch {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ALL_CONFIG_TYPES,
|
|
4
|
+
formatInitResults,
|
|
5
|
+
runInit
|
|
6
|
+
} from "./chunk-L5WU2HTN.js";
|
|
2
7
|
import {
|
|
3
8
|
analyzeImpact,
|
|
4
9
|
autoHeal,
|
|
@@ -9,7 +14,13 @@ import {
|
|
|
9
14
|
getGraphStats,
|
|
10
15
|
searchGraph,
|
|
11
16
|
traceFlow
|
|
12
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-EQ2CE4UC.js";
|
|
18
|
+
import {
|
|
19
|
+
buildDriftMessages,
|
|
20
|
+
getGitDiffStat,
|
|
21
|
+
getSessionStats,
|
|
22
|
+
getUnresolvedCount
|
|
23
|
+
} from "./chunk-JIL533AM.js";
|
|
13
24
|
import {
|
|
14
25
|
addContextNote,
|
|
15
26
|
addSecurityFinding,
|
|
@@ -35,27 +46,16 @@ import {
|
|
|
35
46
|
saveResearchCache,
|
|
36
47
|
updateDecisionStatus,
|
|
37
48
|
updateTodoStatus
|
|
38
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-5N6KXACT.js";
|
|
39
50
|
import {
|
|
40
51
|
formatDecisionTemplate,
|
|
41
52
|
getProactiveMemories,
|
|
42
53
|
recordDecision,
|
|
43
54
|
scoreMemoryRelevance
|
|
44
|
-
} from "./chunk-
|
|
45
|
-
import {
|
|
46
|
-
buildDriftMessages,
|
|
47
|
-
getGitDiffStat,
|
|
48
|
-
getSessionStats,
|
|
49
|
-
getUnresolvedCount
|
|
50
|
-
} from "./chunk-WG47POMW.js";
|
|
55
|
+
} from "./chunk-EIK7T746.js";
|
|
51
56
|
import {
|
|
52
57
|
sessionMemory
|
|
53
|
-
} from "./chunk-
|
|
54
|
-
import {
|
|
55
|
-
ALL_CONFIG_TYPES,
|
|
56
|
-
formatInitResults,
|
|
57
|
-
runInit
|
|
58
|
-
} from "./chunk-5WSPGLXS.js";
|
|
58
|
+
} from "./chunk-SU7BTOND.js";
|
|
59
59
|
import {
|
|
60
60
|
getProjectRoot,
|
|
61
61
|
validateFilePath
|
|
@@ -155,8 +155,8 @@ async function handleInit(_params) {
|
|
|
155
155
|
lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
|
|
156
156
|
lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
|
|
157
157
|
try {
|
|
158
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
159
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
158
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-QX2LBJ7H.js");
|
|
159
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-RC2EO3OB.js");
|
|
160
160
|
const score = await computeSafetyScore(_params.goal);
|
|
161
161
|
const checksStr = JSON.stringify(score.checks);
|
|
162
162
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -329,8 +329,8 @@ async function handleResearches(_params) {
|
|
|
329
329
|
async function handleHealth(_params) {
|
|
330
330
|
sessionMemory.recordToolCall("kuma_context_health", {});
|
|
331
331
|
try {
|
|
332
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
333
|
-
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-
|
|
332
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-QX2LBJ7H.js");
|
|
333
|
+
const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-RC2EO3OB.js");
|
|
334
334
|
const score = await computeSafetyScore();
|
|
335
335
|
const checksStr = JSON.stringify(score.checks);
|
|
336
336
|
await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
|
|
@@ -367,6 +367,8 @@ async function handleMemory(params) {
|
|
|
367
367
|
switch (action) {
|
|
368
368
|
case "decision":
|
|
369
369
|
return handleDecision(params);
|
|
370
|
+
case "mine":
|
|
371
|
+
return handleMine(params);
|
|
370
372
|
case "research_save":
|
|
371
373
|
return handleResearchSave(params);
|
|
372
374
|
case "session":
|
|
@@ -395,7 +397,7 @@ async function handleDecision(params) {
|
|
|
395
397
|
case "template":
|
|
396
398
|
return formatDecisionTemplate();
|
|
397
399
|
case "suggest": {
|
|
398
|
-
const { shouldRecordDecision } = await import("./kumaMemory-
|
|
400
|
+
const { shouldRecordDecision } = await import("./kumaMemory-ME4XWD5Z.js");
|
|
399
401
|
const check = shouldRecordDecision();
|
|
400
402
|
return check.worth ? `\u{1F4A1} Decision suggested: "${check.title}"
|
|
401
403
|
Use kuma_memory({ action: 'decision', title: '...', context: '...', rationale: '...', outcome: '...' }) to record.` : "\u2705 No decision needed at this time.";
|
|
@@ -473,7 +475,7 @@ async function handleSearch(params) {
|
|
|
473
475
|
if (!query) return "\u26A0\uFE0F query or scope parameter required.";
|
|
474
476
|
const limit = params.limit || 20;
|
|
475
477
|
const memResults = sessionMemory.searchMemory(query, limit);
|
|
476
|
-
const { searchGraph: searchGraph2 } = await import("./kumaGraph-
|
|
478
|
+
const { searchGraph: searchGraph2 } = await import("./kumaGraph-5IZ4FDZA.js");
|
|
477
479
|
const graphResults = await searchGraph2(query, Math.min(limit, 10));
|
|
478
480
|
const lines = [`\u{1F50D} **Search Results** \u2014 "${query}"
|
|
479
481
|
\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
|
|
@@ -487,7 +489,8 @@ async function handleSearch(params) {
|
|
|
487
489
|
return lines.join("\n");
|
|
488
490
|
}
|
|
489
491
|
async function handleChanges2(params) {
|
|
490
|
-
|
|
492
|
+
const sinceNum = typeof params.since === "number" ? params.since : typeof params.since === "string" ? parseInt(params.since, 10) || void 0 : void 0;
|
|
493
|
+
return await getChanges({ filePath: params.target, since: sinceNum });
|
|
491
494
|
}
|
|
492
495
|
async function handleTodo(params) {
|
|
493
496
|
if (params.todoId && params.status) {
|
|
@@ -545,6 +548,16 @@ async function handleDecisionLog(params) {
|
|
|
545
548
|
}
|
|
546
549
|
return await listDecisionLog(params.status);
|
|
547
550
|
}
|
|
551
|
+
async function handleMine(params) {
|
|
552
|
+
sessionMemory.recordToolCall("kuma_memory_mine", { scope: params.scope });
|
|
553
|
+
const { mineHistoricalDecisions } = await import("./kumaMiner-GFEGUWGH.js");
|
|
554
|
+
return await mineHistoricalDecisions({
|
|
555
|
+
scope: params.scope,
|
|
556
|
+
since: typeof params.since === "string" ? params.since : void 0,
|
|
557
|
+
confirm: params.confirm,
|
|
558
|
+
limit: params.limit
|
|
559
|
+
});
|
|
560
|
+
}
|
|
548
561
|
|
|
549
562
|
// src/engine/kumaLock.ts
|
|
550
563
|
import fs3 from "fs";
|
|
@@ -1607,6 +1620,8 @@ async function handleSafety(params) {
|
|
|
1607
1620
|
switch (action) {
|
|
1608
1621
|
case "guard":
|
|
1609
1622
|
return handleGuard(params);
|
|
1623
|
+
case "verify":
|
|
1624
|
+
return handleVerify(params);
|
|
1610
1625
|
case "check":
|
|
1611
1626
|
return handleCheck(params);
|
|
1612
1627
|
case "audit":
|
|
@@ -1670,7 +1685,7 @@ async function handleLock(params) {
|
|
|
1670
1685
|
}
|
|
1671
1686
|
async function handleHealth2(_params) {
|
|
1672
1687
|
try {
|
|
1673
|
-
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-
|
|
1688
|
+
const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-QX2LBJ7H.js");
|
|
1674
1689
|
const score = await computeSafetyScore();
|
|
1675
1690
|
await saveHealthSnapshot(score.score, score.risk, JSON.stringify(score.checks), score.summary);
|
|
1676
1691
|
return formatSafetyScore(score);
|
|
@@ -1742,6 +1757,15 @@ async function handleGitignore(_params) {
|
|
|
1742
1757
|
sessionMemory.recordToolCall("kuma_safety_gitignore", {});
|
|
1743
1758
|
return await ensureGitignore();
|
|
1744
1759
|
}
|
|
1760
|
+
async function handleVerify(params) {
|
|
1761
|
+
sessionMemory.recordToolCall("kuma_safety_verify", { scope: params.scope || params.filePath });
|
|
1762
|
+
const { runAutoVerification } = await import("./kumaVerifier-VYW5WH6V.js");
|
|
1763
|
+
return await runAutoVerification({
|
|
1764
|
+
scope: params.scope || params.filePath,
|
|
1765
|
+
force: params.force,
|
|
1766
|
+
timeoutMs: 3e4
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1745
1769
|
|
|
1746
1770
|
// src/manifest.ts
|
|
1747
1771
|
function registerAllTools(server) {
|
|
@@ -1774,14 +1798,15 @@ function registerAllTools(server) {
|
|
|
1774
1798
|
);
|
|
1775
1799
|
server.tool(
|
|
1776
1800
|
"kuma_memory",
|
|
1777
|
-
"Record and retrieve project knowledge. Actions: decision (ADR-style record/template/suggest), 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).",
|
|
1801
|
+
"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).",
|
|
1778
1802
|
{
|
|
1779
|
-
action: z.enum(["decision", "research_save", "session", "heal", "search", "changes", "todo", "context", "benchmark", "decision_log"]).describe("Memory action: decision=ADR, research_save=save, session=summary, heal=repair, search=search, changes=log, todo=manage todos, context=inject notes, benchmark=capture/diff, decision_log=manage decisions"),
|
|
1780
|
-
scope: z.string().optional().describe("Scope for research_save/search/todo/context"),
|
|
1803
|
+
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"),
|
|
1804
|
+
scope: z.string().optional().describe("Scope for research_save/search/todo/context/mine"),
|
|
1781
1805
|
query: z.string().optional().describe("Search query for search action"),
|
|
1782
1806
|
content: z.string().optional().describe("Content/notes for research_save / context"),
|
|
1783
1807
|
record: z.string().optional().describe("JSON record string for research_save"),
|
|
1784
1808
|
confidence: z.number().min(0).max(1).optional().describe("Confidence for research_save (0-1)"),
|
|
1809
|
+
confirm: z.boolean().optional().describe("Confirm and record candidates automatically for mine action"),
|
|
1785
1810
|
// Decision params
|
|
1786
1811
|
decisionAction: z.enum(["template", "suggest", "record"]).optional().describe("Decision sub-action"),
|
|
1787
1812
|
title: z.string().optional().describe("Decision/todo/benchmark title"),
|
|
@@ -1817,6 +1842,7 @@ function registerAllTools(server) {
|
|
|
1817
1842
|
content: params.content,
|
|
1818
1843
|
record: params.record,
|
|
1819
1844
|
confidence: params.confidence,
|
|
1845
|
+
confirm: params.confirm,
|
|
1820
1846
|
decisionAction: params.decisionAction,
|
|
1821
1847
|
title: params.title,
|
|
1822
1848
|
context: params.context,
|
|
@@ -1836,9 +1862,11 @@ function registerAllTools(server) {
|
|
|
1836
1862
|
);
|
|
1837
1863
|
server.tool(
|
|
1838
1864
|
"kuma_safety",
|
|
1839
|
-
"Safety checks, policy enforcement, security scanning, and project hygiene. Actions: guard (anti-pattern detection), 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).",
|
|
1865
|
+
"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).",
|
|
1840
1866
|
{
|
|
1841
|
-
action: z.enum(["guard", "check", "audit", "lock", "health", "override", "security", "gc", "doctor", "portability", "gitignore"]).describe("Safety action: guard=anti-patterns, 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, override=bypass"),
|
|
1867
|
+
action: z.enum(["guard", "verify", "check", "audit", "lock", "health", "override", "security", "gc", "doctor", "portability", "gitignore"]).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, override=bypass"),
|
|
1868
|
+
// Verify params
|
|
1869
|
+
scope: z.string().optional().describe("Scope for verify or context (e.g. 'auth', file path)"),
|
|
1842
1870
|
// Guard params
|
|
1843
1871
|
guardGoal: z.string().optional().describe("Goal for guard check"),
|
|
1844
1872
|
guardCheck: z.enum(["all", "anti-pattern", "loop", "drift", "context"]).optional().describe("Guard check type"),
|
|
@@ -1866,6 +1894,7 @@ function registerAllTools(server) {
|
|
|
1866
1894
|
guardGoal: params.guardGoal,
|
|
1867
1895
|
guardCheck: params.guardCheck,
|
|
1868
1896
|
filePath: params.filePath,
|
|
1897
|
+
scope: params.scope,
|
|
1869
1898
|
command: params.command,
|
|
1870
1899
|
toolName: params.toolName,
|
|
1871
1900
|
riskLevel: params.riskLevel,
|
|
@@ -2013,7 +2042,7 @@ async function main() {
|
|
|
2013
2042
|
});
|
|
2014
2043
|
(async () => {
|
|
2015
2044
|
try {
|
|
2016
|
-
const { generateInitMdContent } = await import("./init-
|
|
2045
|
+
const { generateInitMdContent } = await import("./init-AJAESUZZ.js");
|
|
2017
2046
|
const fs7 = await import("fs");
|
|
2018
2047
|
const path7 = await import("path");
|
|
2019
2048
|
const initMdPath = path7.resolve(process.cwd(), ".kuma/init.md");
|
|
@@ -2071,7 +2100,7 @@ async function main() {
|
|
|
2071
2100
|
console.error(`[${SERVER_NAME}] \u2705 Restored session (${sessionInfo.toolCallCount} previous tool calls)`);
|
|
2072
2101
|
}
|
|
2073
2102
|
try {
|
|
2074
|
-
const { buildFromSessionMemory } = await import("./kumaGraph-
|
|
2103
|
+
const { buildFromSessionMemory } = await import("./kumaGraph-5IZ4FDZA.js");
|
|
2075
2104
|
const edgeCount = await buildFromSessionMemory();
|
|
2076
2105
|
if (edgeCount > 0) {
|
|
2077
2106
|
console.error(`[${SERVER_NAME}] \u2705 Graph auto-populated with ${edgeCount} entries from session memory`);
|
|
@@ -2080,7 +2109,7 @@ async function main() {
|
|
|
2080
2109
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Graph auto-population: ${err}`);
|
|
2081
2110
|
}
|
|
2082
2111
|
try {
|
|
2083
|
-
const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-
|
|
2112
|
+
const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-RC2EO3OB.js");
|
|
2084
2113
|
const db = await getDb2();
|
|
2085
2114
|
db.run(
|
|
2086
2115
|
`INSERT INTO sessions (started_at, goal, tool_calls) VALUES (?, ?, ?)`,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getChanges,
|
|
9
9
|
getDb,
|
|
10
10
|
getFileSummary,
|
|
11
|
+
getLatestVerifications,
|
|
11
12
|
getResearchCache,
|
|
12
13
|
getSecurityFindings,
|
|
13
14
|
listContextNotes,
|
|
@@ -24,9 +25,10 @@ import {
|
|
|
24
25
|
saveFileSummary,
|
|
25
26
|
saveHealthSnapshot,
|
|
26
27
|
saveResearchCache,
|
|
28
|
+
saveVerification,
|
|
27
29
|
updateDecisionStatus,
|
|
28
30
|
updateTodoStatus
|
|
29
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5N6KXACT.js";
|
|
30
32
|
import "./chunk-E2KFPEBT.js";
|
|
31
33
|
export {
|
|
32
34
|
addContextNote,
|
|
@@ -38,6 +40,7 @@ export {
|
|
|
38
40
|
getChanges,
|
|
39
41
|
getDb,
|
|
40
42
|
getFileSummary,
|
|
43
|
+
getLatestVerifications,
|
|
41
44
|
getResearchCache,
|
|
42
45
|
getSecurityFindings,
|
|
43
46
|
listContextNotes,
|
|
@@ -54,6 +57,7 @@ export {
|
|
|
54
57
|
saveFileSummary,
|
|
55
58
|
saveHealthSnapshot,
|
|
56
59
|
saveResearchCache,
|
|
60
|
+
saveVerification,
|
|
57
61
|
updateDecisionStatus,
|
|
58
62
|
updateTodoStatus
|
|
59
63
|
};
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
recordDecision,
|
|
5
5
|
scoreMemoryRelevance,
|
|
6
6
|
shouldRecordDecision
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-EIK7T746.js";
|
|
8
|
+
import "./chunk-SU7BTOND.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
export {
|
|
11
11
|
formatDecisionTemplate,
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
recordDecisionLog
|
|
3
|
+
} from "./chunk-5N6KXACT.js";
|
|
4
|
+
import {
|
|
5
|
+
recordDecision
|
|
6
|
+
} from "./chunk-EIK7T746.js";
|
|
7
|
+
import "./chunk-SU7BTOND.js";
|
|
8
|
+
import "./chunk-E2KFPEBT.js";
|
|
9
|
+
|
|
10
|
+
// src/engine/kumaMiner.ts
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { execSync } from "child_process";
|
|
14
|
+
import fastGlob from "fast-glob";
|
|
15
|
+
async function mineHistoricalDecisions(options = {}) {
|
|
16
|
+
const root = process.cwd();
|
|
17
|
+
const limit = options.limit || 15;
|
|
18
|
+
const candidates = [];
|
|
19
|
+
try {
|
|
20
|
+
const sinceFlag = options.since ? `--since="${options.since}"` : `--since="1 year"`;
|
|
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" });
|
|
23
|
+
const keywords = ["fix", "revert", "hack", "workaround", "urgent", "hotfix", "don't touch", "temporary", "deprecated", "workaround"];
|
|
24
|
+
const lines = gitOutput.split("\n").filter(Boolean);
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
const parts = line.split("|");
|
|
27
|
+
if (parts.length < 4) continue;
|
|
28
|
+
const [hash, author, date, subject] = parts;
|
|
29
|
+
const lowerSubj = subject.toLowerCase();
|
|
30
|
+
const matchedKeyword = keywords.find((kw) => lowerSubj.includes(kw));
|
|
31
|
+
if (matchedKeyword) {
|
|
32
|
+
let changedFiles = "";
|
|
33
|
+
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(", ");
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
candidates.push({
|
|
38
|
+
id: `git-${hash}`,
|
|
39
|
+
type: "git_commit",
|
|
40
|
+
title: `[Git Commit] ${subject}`,
|
|
41
|
+
context: `Commit ${hash} by ${author} on ${date}.${changedFiles ? ` Files touched: ${changedFiles}` : ""}`,
|
|
42
|
+
rationale: `Historical signal pattern "${matchedKeyword}" found in commit message.`,
|
|
43
|
+
commitHash: hash,
|
|
44
|
+
pattern: matchedKeyword
|
|
45
|
+
});
|
|
46
|
+
if (candidates.length >= limit) break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
if (candidates.length < limit) {
|
|
52
|
+
try {
|
|
53
|
+
const scopePattern = options.scope ? `**/*${options.scope}*.*` : "**/*.{ts,js,py,go,rs,java,md}";
|
|
54
|
+
const files = await fastGlob(scopePattern, {
|
|
55
|
+
cwd: root,
|
|
56
|
+
ignore: ["node_modules/**", "dist/**", ".kuma/**", ".git/**", "coverage/**"],
|
|
57
|
+
onlyFiles: true
|
|
58
|
+
});
|
|
59
|
+
const commentPatterns = [
|
|
60
|
+
{ tag: "HACK", regex: /\/\/\s*HACK:?\s*(.*)|#\s*HACK:?\s*(.*)/i },
|
|
61
|
+
{ tag: "FIXME", regex: /\/\/\s*FIXME:?\s*(.*)|#\s*FIXME:?\s*(.*)/i },
|
|
62
|
+
{ tag: "TODO", regex: /\/\/\s*TODO:?\s*(.*)|#\s*TODO:?\s*(.*)/i },
|
|
63
|
+
{ tag: "XXX", regex: /\/\/\s*XXX:?\s*(.*)|#\s*XXX:?\s*(.*)/i },
|
|
64
|
+
{ tag: "WARNING", regex: /\/\/\s*WARNING:?\s*(.*)|#\s*WARNING:?\s*(.*)/i }
|
|
65
|
+
];
|
|
66
|
+
for (const file of files.slice(0, 50)) {
|
|
67
|
+
if (candidates.length >= limit) break;
|
|
68
|
+
const fullPath = path.join(root, file);
|
|
69
|
+
if (!fs.existsSync(fullPath)) continue;
|
|
70
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
71
|
+
const lines = content.split("\n");
|
|
72
|
+
for (let i = 0; i < lines.length; i++) {
|
|
73
|
+
const l = lines[i];
|
|
74
|
+
for (const cp of commentPatterns) {
|
|
75
|
+
const match = cp.regex.exec(l);
|
|
76
|
+
if (match) {
|
|
77
|
+
const text = (match[1] || match[2] || l).trim();
|
|
78
|
+
candidates.push({
|
|
79
|
+
id: `comment-${file}-${i + 1}`,
|
|
80
|
+
type: "inline_comment",
|
|
81
|
+
title: `[${cp.tag}] ${file}:${i + 1}`,
|
|
82
|
+
context: `Inline comment marker "${cp.tag}" found in file "${file}" at line ${i + 1}.`,
|
|
83
|
+
rationale: text || l.trim(),
|
|
84
|
+
filePath: file,
|
|
85
|
+
lineNumber: i + 1,
|
|
86
|
+
pattern: cp.tag
|
|
87
|
+
});
|
|
88
|
+
if (candidates.length >= limit) break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (candidates.length >= limit) break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (options.confirm && candidates.length > 0) {
|
|
98
|
+
let recordedCount = 0;
|
|
99
|
+
for (const c of candidates) {
|
|
100
|
+
await recordDecisionLog({
|
|
101
|
+
title: c.title,
|
|
102
|
+
context: c.context,
|
|
103
|
+
rationale: c.rationale,
|
|
104
|
+
outcome: `Mined from ${c.type} (${c.pattern})`,
|
|
105
|
+
status: "proposed"
|
|
106
|
+
});
|
|
107
|
+
await recordDecision({
|
|
108
|
+
title: c.title,
|
|
109
|
+
context: c.context,
|
|
110
|
+
options: [],
|
|
111
|
+
rationale: c.rationale,
|
|
112
|
+
outcome: `Mined from ${c.type}`,
|
|
113
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
114
|
+
});
|
|
115
|
+
recordedCount++;
|
|
116
|
+
}
|
|
117
|
+
return `\u26CF\uFE0F **Mined & Recorded ${recordedCount} Decisions** into decision log & graph.
|
|
118
|
+
Use \`kuma_memory({ action: 'decision_log' })\` to view all active & proposed decisions.`;
|
|
119
|
+
}
|
|
120
|
+
if (candidates.length === 0) {
|
|
121
|
+
return "\u26CF\uFE0F **Decision Mining**: No significant historical decision markers (HACK/FIXME/git fix) found for the requested scope.";
|
|
122
|
+
}
|
|
123
|
+
const report = [
|
|
124
|
+
`\u26CF\uFE0F **Decision Mining Candidates** (${candidates.length} candidate(s) found)`,
|
|
125
|
+
"\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\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",
|
|
126
|
+
"Review these draft historical decisions mined from git history & inline comments:",
|
|
127
|
+
""
|
|
128
|
+
];
|
|
129
|
+
candidates.forEach((c, idx) => {
|
|
130
|
+
report.push(`**${idx + 1}. ${c.title}**`);
|
|
131
|
+
report.push(` \u2022 **Type**: \`${c.type}\` | **Signal**: \`${c.pattern}\``);
|
|
132
|
+
report.push(` \u2022 **Context**: ${c.context}`);
|
|
133
|
+
report.push(` \u2022 **Rationale**: ${c.rationale}`);
|
|
134
|
+
report.push("");
|
|
135
|
+
});
|
|
136
|
+
report.push("\u{1F4A1} *To accept and record these mined decisions into knowledge graph & decision.md:*");
|
|
137
|
+
report.push(`Run: \`kuma_memory({ action: 'mine', confirm: true${options.scope ? `, scope: '${options.scope}'` : ""} })\``);
|
|
138
|
+
return report.join("\n");
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
mineHistoricalDecisions
|
|
142
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
saveVerification
|
|
3
|
+
} from "./chunk-5N6KXACT.js";
|
|
4
|
+
import {
|
|
5
|
+
sessionMemory
|
|
6
|
+
} from "./chunk-SU7BTOND.js";
|
|
7
|
+
import "./chunk-E2KFPEBT.js";
|
|
8
|
+
|
|
9
|
+
// src/engine/kumaVerifier.ts
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { exec } from "child_process";
|
|
13
|
+
function detectTestRunner(root = process.cwd()) {
|
|
14
|
+
const pkgPath = path.join(root, "package.json");
|
|
15
|
+
if (fs.existsSync(pkgPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
18
|
+
if (pkg.scripts && pkg.scripts.test) {
|
|
19
|
+
if (fs.existsSync(path.join(root, "pnpm-lock.yaml"))) {
|
|
20
|
+
return { runner: "pnpm", baseCommand: "pnpm test" };
|
|
21
|
+
}
|
|
22
|
+
if (fs.existsSync(path.join(root, "yarn.lock"))) {
|
|
23
|
+
return { runner: "yarn", baseCommand: "yarn test" };
|
|
24
|
+
}
|
|
25
|
+
return { runner: "npm", baseCommand: "npm test" };
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (fs.existsSync(path.join(root, "pytest.ini")) || fs.existsSync(path.join(root, "pyproject.toml"))) {
|
|
31
|
+
return { runner: "pytest", baseCommand: "pytest" };
|
|
32
|
+
}
|
|
33
|
+
if (fs.existsSync(path.join(root, "Cargo.toml"))) {
|
|
34
|
+
return { runner: "cargo", baseCommand: "cargo test" };
|
|
35
|
+
}
|
|
36
|
+
if (fs.existsSync(path.join(root, "go.mod"))) {
|
|
37
|
+
return { runner: "go", baseCommand: "go test ./..." };
|
|
38
|
+
}
|
|
39
|
+
if (fs.existsSync(path.join(root, "Makefile"))) {
|
|
40
|
+
return { runner: "make", baseCommand: "make test" };
|
|
41
|
+
}
|
|
42
|
+
return { runner: "unknown", baseCommand: "npm test" };
|
|
43
|
+
}
|
|
44
|
+
async function runAutoVerification(options = {}) {
|
|
45
|
+
const startTime = Date.now();
|
|
46
|
+
const root = process.cwd();
|
|
47
|
+
const scope = options.scope || "session-impact";
|
|
48
|
+
const timeoutMs = options.timeoutMs || 3e4;
|
|
49
|
+
const { runner, baseCommand } = detectTestRunner(root);
|
|
50
|
+
let testFiles = [];
|
|
51
|
+
const modified = sessionMemory.getModifiedFiles().map((f) => f.filePath);
|
|
52
|
+
if (options.scope) {
|
|
53
|
+
const term = options.scope.toLowerCase();
|
|
54
|
+
testFiles = modified.filter((f) => f.toLowerCase().includes(term));
|
|
55
|
+
if (testFiles.length === 0) {
|
|
56
|
+
try {
|
|
57
|
+
const { default: glob } = await import("fast-glob");
|
|
58
|
+
const found = await glob([`**/*${term}*test*.*`, `**/*test*/*${term}*.*`], { cwd: root, ignore: ["node_modules/**", "dist/**"] });
|
|
59
|
+
testFiles = found;
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
testFiles = modified.filter((f) => f.includes(".test.") || f.includes(".spec.") || f.includes("_test."));
|
|
65
|
+
}
|
|
66
|
+
let fullCommand = baseCommand;
|
|
67
|
+
if (testFiles.length > 0 && runner === "npm") {
|
|
68
|
+
fullCommand = `${baseCommand} -- ${testFiles.map((f) => `"${f}"`).join(" ")}`;
|
|
69
|
+
}
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
exec(fullCommand, { cwd: root, timeout: timeoutMs }, async (error, stdout, stderr) => {
|
|
72
|
+
const durationMs = Date.now() - startTime;
|
|
73
|
+
const rawOutput = (stdout + "\n" + stderr).trim();
|
|
74
|
+
const passed = !error;
|
|
75
|
+
const truncatedOutput = rawOutput.length > 2e3 ? rawOutput.substring(rawOutput.length - 2e3) : rawOutput;
|
|
76
|
+
await saveVerification(scope, runner, fullCommand, passed, truncatedOutput, durationMs);
|
|
77
|
+
const statusSymbol = passed ? "\u2705" : "\u{1F534}";
|
|
78
|
+
const summaryHeader = `${statusSymbol} **Auto-Verification ${passed ? "PASSED" : "FAILED"}**
|
|
79
|
+
\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\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`;
|
|
80
|
+
const details = [
|
|
81
|
+
summaryHeader,
|
|
82
|
+
`\u{1F6E0}\uFE0F **Runner**: \`${runner}\``,
|
|
83
|
+
`\u{1F4BB} **Command**: \`${fullCommand}\``,
|
|
84
|
+
`\u{1F3AF} **Scope**: \`${scope}\`${testFiles.length > 0 ? ` (${testFiles.length} file(s) matched)` : ""}`,
|
|
85
|
+
`\u23F1\uFE0F **Duration**: ${durationMs}ms`,
|
|
86
|
+
""
|
|
87
|
+
];
|
|
88
|
+
if (!passed) {
|
|
89
|
+
details.push("\u26A0\uFE0F **BLOCKER**: Verification failed! Please fix test failures before shipping or continuing edits.");
|
|
90
|
+
details.push("```text");
|
|
91
|
+
details.push(rawOutput.substring(0, 1500));
|
|
92
|
+
details.push("```");
|
|
93
|
+
} else {
|
|
94
|
+
details.push("\u{1F389} All scoped tests passed cleanly!");
|
|
95
|
+
if (rawOutput) {
|
|
96
|
+
details.push("```text");
|
|
97
|
+
details.push(rawOutput.substring(0, 800));
|
|
98
|
+
details.push("```");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
resolve(details.join("\n"));
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
detectTestRunner,
|
|
107
|
+
runAutoVerification
|
|
108
|
+
};
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
getGitDiffStat,
|
|
3
3
|
getSessionStats,
|
|
4
4
|
getUnresolvedCount
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-JIL533AM.js";
|
|
6
6
|
import {
|
|
7
7
|
sessionMemory
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-SU7BTOND.js";
|
|
9
9
|
import "./chunk-E2KFPEBT.js";
|
|
10
10
|
|
|
11
11
|
// src/engine/safetyScore.ts
|
|
@@ -52,7 +52,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
52
52
|
totalScore += 20;
|
|
53
53
|
}
|
|
54
54
|
try {
|
|
55
|
-
const { getDb } = await import("./kumaDb-
|
|
55
|
+
const { getDb } = await import("./kumaDb-RC2EO3OB.js");
|
|
56
56
|
const db = await getDb();
|
|
57
57
|
const nodeCount = db.exec("SELECT COUNT(*) as c FROM nodes")[0]?.values[0][0] ?? 0;
|
|
58
58
|
const edgeCount = db.exec("SELECT COUNT(*) as c FROM edges")[0]?.values[0][0] ?? 0;
|
|
@@ -83,7 +83,7 @@ async function computeSafetyScore(inputGoal) {
|
|
|
83
83
|
totalScore += 5;
|
|
84
84
|
}
|
|
85
85
|
try {
|
|
86
|
-
const { getDb } = await import("./kumaDb-
|
|
86
|
+
const { getDb } = await import("./kumaDb-RC2EO3OB.js");
|
|
87
87
|
const db = await getDb();
|
|
88
88
|
const researchCount = db.exec("SELECT COUNT(*) as c FROM research_cache")[0]?.values[0][0] ?? 0;
|
|
89
89
|
checks.push({
|
|
@@ -106,14 +106,38 @@ async function computeSafetyScore(inputGoal) {
|
|
|
106
106
|
const allFailures = stats.failedFiles.flatMap((f) => f.failures);
|
|
107
107
|
const testFailures = allFailures.filter((f) => f.error.toLowerCase().includes("test") || f.error.toLowerCase().includes("fail"));
|
|
108
108
|
const hasRunTests = stats.hasRunTests;
|
|
109
|
-
|
|
109
|
+
let latestVerif = null;
|
|
110
|
+
try {
|
|
111
|
+
const { getLatestVerifications } = await import("./kumaDb-RC2EO3OB.js");
|
|
112
|
+
const verifs = await getLatestVerifications(1);
|
|
113
|
+
if (verifs.length > 0) latestVerif = verifs[0];
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
if (!hasRunTests && !latestVerif) {
|
|
110
117
|
checks.push({
|
|
111
118
|
label: "Tests Status",
|
|
112
119
|
status: "warn",
|
|
113
|
-
message: "No tests run yet this session",
|
|
120
|
+
message: "No tests run yet this session \u2014 run kuma_safety({ action: 'verify' })",
|
|
114
121
|
weight: 10
|
|
115
122
|
});
|
|
116
123
|
totalScore += 10;
|
|
124
|
+
} else if (latestVerif) {
|
|
125
|
+
if (latestVerif.passed) {
|
|
126
|
+
checks.push({
|
|
127
|
+
label: "Tests Status",
|
|
128
|
+
status: "pass",
|
|
129
|
+
message: `Auto-verified passed (${latestVerif.scope})`,
|
|
130
|
+
weight: 15
|
|
131
|
+
});
|
|
132
|
+
totalScore += 15;
|
|
133
|
+
} else {
|
|
134
|
+
checks.push({
|
|
135
|
+
label: "Tests Status",
|
|
136
|
+
status: "fail",
|
|
137
|
+
message: `Auto-verification failed (${latestVerif.scope}) \u2014 fix before shipping`,
|
|
138
|
+
weight: 0
|
|
139
|
+
});
|
|
140
|
+
}
|
|
117
141
|
} else if (testFailures.length === 0 && unresolvedCount === 0) {
|
|
118
142
|
checks.push({
|
|
119
143
|
label: "Tests Status",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
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",
|