@plumpslabs/kuma 2.3.5 → 2.3.6

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.
@@ -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-FQ3YKJ6K.js");
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
- for (let i = 0; i < lines.length; i++) {
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
- toolCounts.set(call.toolName, (toolCounts.get(call.toolName) ?? 0) + 1);
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-FQ3YKJ6K.js");
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 {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionMemory
3
- } from "./chunk-PH4JKL6T.js";
3
+ } from "./chunk-BI7KD3SG.js";
4
4
  import {
5
5
  getProjectRoot
6
6
  } from "./chunk-E2KFPEBT.js";
@@ -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
- try {
43
- const kumaDir = getKumaDir();
44
- const dbPath = path.join(kumaDir, DB_FILENAME);
45
- const data = d.export();
46
- const buffer = Buffer.from(data);
47
- fs.writeFileSync(dbPath, buffer);
48
- } catch (err) {
49
- console.error(`[KumaDB] Failed to save database: ${err}`);
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-2NBESJTH.js";
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-PGYVS3BK.js");
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
- walk2(root);
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: 1024 * 1024 }
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-PH4JKL6T.js";
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-ABVEVF4M.js");
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,13 @@ import {
14
14
  getGraphStats,
15
15
  searchGraph,
16
16
  traceFlow
17
- } from "./chunk-LLAPHR5W.js";
17
+ } from "./chunk-K64NSHBR.js";
18
18
  import {
19
19
  buildDriftMessages,
20
20
  getGitDiffStat,
21
21
  getSessionStats,
22
22
  getUnresolvedCount
23
- } from "./chunk-ZIFLGJ5R.js";
23
+ } from "./chunk-FOQQ2CSL.js";
24
24
  import {
25
25
  addContextNote,
26
26
  addSecurityFinding,
@@ -46,16 +46,16 @@ import {
46
46
  saveResearchCache,
47
47
  updateDecisionStatus,
48
48
  updateTodoStatus
49
- } from "./chunk-2NBESJTH.js";
49
+ } from "./chunk-GDNAWLHF.js";
50
50
  import {
51
51
  formatDecisionTemplate,
52
52
  getProactiveMemories,
53
53
  recordDecision,
54
54
  scoreMemoryRelevance
55
- } from "./chunk-6QX4LBHV.js";
55
+ } from "./chunk-X5TPBDKO.js";
56
56
  import {
57
57
  sessionMemory
58
- } from "./chunk-PH4JKL6T.js";
58
+ } from "./chunk-BI7KD3SG.js";
59
59
  import {
60
60
  getProjectRoot,
61
61
  validateFilePath
@@ -111,7 +111,13 @@ 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"
115
121
  };
116
122
  async function handleContext(params) {
117
123
  const rawAction = params.action || "init";
@@ -134,8 +140,10 @@ async function handleContext(params) {
134
140
  return handleResearches(params);
135
141
  case "health":
136
142
  return handleHealth(params);
143
+ case "sync":
144
+ return handleSync(params);
137
145
  default:
138
- return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health`;
146
+ return `Unknown action "${action}". Use: init, research, impact, navigate, changes, rollback, researches, health, sync`;
139
147
  }
140
148
  }
141
149
  async function handleInit(_params) {
@@ -196,8 +204,8 @@ async function handleInit(_params) {
196
204
  lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
197
205
  lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
198
206
  try {
199
- const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-VYT6OMVG.js");
200
- const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-FQ3YKJ6K.js");
207
+ const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
208
+ const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
201
209
  const score = await computeSafetyScore(_params.goal);
202
210
  const checksStr = JSON.stringify(score.checks);
203
211
  await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
@@ -370,8 +378,8 @@ async function handleResearches(_params) {
370
378
  async function handleHealth(_params) {
371
379
  sessionMemory.recordToolCall("kuma_context_health", {});
372
380
  try {
373
- const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-VYT6OMVG.js");
374
- const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-FQ3YKJ6K.js");
381
+ const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
382
+ const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
375
383
  const score = await computeSafetyScore();
376
384
  const checksStr = JSON.stringify(score.checks);
377
385
  await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
@@ -380,6 +388,55 @@ async function handleHealth(_params) {
380
388
  return `Error computing health: ${err}`;
381
389
  }
382
390
  }
391
+ async function handleSync(params) {
392
+ sessionMemory.setGoal(params.goal || "Sync session");
393
+ sessionMemory.recordToolCall("kuma_context_sync", { goal: params.goal });
394
+ const lines = [
395
+ "\u{1F504} **Kuma Sync \u2014 Unified State**",
396
+ `\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`,
397
+ "",
398
+ `\u{1F4C1} Project: ${getProjectRoot().split("/").pop() || "unknown"}`,
399
+ `\u{1F550} Session: ${(/* @__PURE__ */ new Date()).toISOString()}`,
400
+ ""
401
+ ];
402
+ const summary = sessionMemory.getSummary();
403
+ lines.push("**Session State**");
404
+ lines.push(` \u{1F3AF} Goal: ${summary.currentGoal || "not set"}`);
405
+ lines.push(` \u{1F4DD} Modified: ${summary.modifiedFiles?.length || 0} file(s)`);
406
+ lines.push(` \u{1F6E0}\uFE0F Tool calls: ${summary.toolCallCount}`);
407
+ lines.push("");
408
+ try {
409
+ const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
410
+ const { saveHealthSnapshot: saveHealthSnapshot2 } = await import("./kumaDb-DJUDLYBJ.js");
411
+ const score = await computeSafetyScore(params.goal);
412
+ const checksStr = JSON.stringify(score.checks);
413
+ await saveHealthSnapshot2(score.score, score.risk, checksStr, score.summary);
414
+ lines.push("**Health Score**");
415
+ lines.push(formatSafetyScore(score));
416
+ } catch {
417
+ lines.push("**Health Score**: \u26A0\uFE0F Could not compute");
418
+ }
419
+ lines.push("");
420
+ try {
421
+ const { getGraphStats: getGraphStats2 } = await import("./kumaGraph-35TAIBWD.js");
422
+ lines.push("**Knowledge Graph**");
423
+ lines.push(await getGraphStats2());
424
+ } catch {
425
+ }
426
+ lines.push("");
427
+ try {
428
+ const { getProactiveMemories: getProactiveMemories2 } = await import("./kumaMemory-5SR3TGRL.js");
429
+ const memories = getProactiveMemories2();
430
+ if (memories) {
431
+ lines.push("**Relevant Memories**");
432
+ lines.push(memories);
433
+ lines.push("");
434
+ }
435
+ } catch {
436
+ }
437
+ lines.push("\u{1F4A1} Sync complete \u2014 all state captured in a single roundtrip.");
438
+ return lines.join("\n");
439
+ }
383
440
  function computeProjectHash(scope) {
384
441
  try {
385
442
  const root = getProjectRoot();
@@ -501,7 +558,7 @@ async function handleDecision(params) {
501
558
  case "template":
502
559
  return formatDecisionTemplate();
503
560
  case "suggest": {
504
- const { shouldRecordDecision } = await import("./kumaMemory-DDZQXMFZ.js");
561
+ const { shouldRecordDecision } = await import("./kumaMemory-5SR3TGRL.js");
505
562
  const check = shouldRecordDecision();
506
563
  return check.worth ? `\u{1F4A1} Decision suggested: "${check.title}"
507
564
  Use kuma_memory({ action: 'decision', title: '...', context: '...', rationale: '...', outcome: '...' }) to record.` : "\u2705 No decision needed at this time.";
@@ -579,7 +636,7 @@ async function handleSearch(params) {
579
636
  if (!query) return "\u26A0\uFE0F query or scope parameter required.";
580
637
  const limit = params.limit || 20;
581
638
  const memResults = sessionMemory.searchMemory(query, limit);
582
- const { searchGraph: searchGraph2 } = await import("./kumaGraph-ABVEVF4M.js");
639
+ const { searchGraph: searchGraph2 } = await import("./kumaGraph-35TAIBWD.js");
583
640
  const graphResults = await searchGraph2(query, Math.min(limit, 10));
584
641
  const lines = [`\u{1F50D} **Search Results** \u2014 "${query}"
585
642
  \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
@@ -654,7 +711,7 @@ async function handleDecisionLog(params) {
654
711
  }
655
712
  async function handleMine(params) {
656
713
  sessionMemory.recordToolCall("kuma_memory_mine", { scope: params.scope });
657
- const { mineHistoricalDecisions } = await import("./kumaMiner-GP6BITKV.js");
714
+ const { mineHistoricalDecisions } = await import("./kumaMiner-BIDSZE3Q.js");
658
715
  return await mineHistoricalDecisions({
659
716
  scope: params.scope,
660
717
  since: typeof params.since === "string" ? params.since : void 0,
@@ -1791,7 +1848,7 @@ async function handleLock(params) {
1791
1848
  }
1792
1849
  async function handleHealth2(_params) {
1793
1850
  try {
1794
- const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-VYT6OMVG.js");
1851
+ const { computeSafetyScore, formatSafetyScore } = await import("./safetyScore-PJGRRBWP.js");
1795
1852
  const score = await computeSafetyScore();
1796
1853
  await saveHealthSnapshot(score.score, score.risk, JSON.stringify(score.checks), score.summary);
1797
1854
  return formatSafetyScore(score);
@@ -1905,7 +1962,7 @@ async function handleVerify(params) {
1905
1962
  }
1906
1963
  _lastVerifyCall = now;
1907
1964
  sessionMemory.recordToolCall("kuma_safety_verify", { scope: params.scope || params.filePath });
1908
- const { runAutoVerification } = await import("./kumaVerifier-7TFWLEPG.js");
1965
+ const { runAutoVerification } = await import("./kumaVerifier-B4D7NOFT.js");
1909
1966
  return await runAutoVerification({
1910
1967
  scope: params.scope || params.filePath,
1911
1968
  force: params.force,
@@ -2104,7 +2161,7 @@ async function main() {
2104
2161
  console.error("");
2105
2162
  let killedCount = 0;
2106
2163
  try {
2107
- const { getRunningVerificationPid } = await import("./kumaVerifier-7TFWLEPG.js");
2164
+ const { getRunningVerificationPid } = await import("./kumaVerifier-B4D7NOFT.js");
2108
2165
  const pid = getRunningVerificationPid();
2109
2166
  if (pid) {
2110
2167
  try {
@@ -2343,7 +2400,7 @@ async function main() {
2343
2400
  console.error(`[${SERVER_NAME}] \u2705 Restored session (${sessionInfo.toolCallCount} previous tool calls)`);
2344
2401
  }
2345
2402
  try {
2346
- const { buildFromSessionMemory } = await import("./kumaGraph-ABVEVF4M.js");
2403
+ const { buildFromSessionMemory } = await import("./kumaGraph-35TAIBWD.js");
2347
2404
  const edgeCount = await buildFromSessionMemory();
2348
2405
  if (edgeCount > 0) {
2349
2406
  console.error(`[${SERVER_NAME}] \u2705 Graph auto-populated with ${edgeCount} entries from session memory`);
@@ -2363,7 +2420,7 @@ async function main() {
2363
2420
  console.error(`[${SERVER_NAME}] \u26A0\uFE0F Scratch directory setup: ${err}`);
2364
2421
  }
2365
2422
  try {
2366
- const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-FQ3YKJ6K.js");
2423
+ const { getDb: getDb2, saveDb: saveDb2 } = await import("./kumaDb-DJUDLYBJ.js");
2367
2424
  const db = await getDb2();
2368
2425
  db.run(
2369
2426
  `INSERT INTO sessions (started_at, goal, tool_calls) VALUES (?, ?, ?)`,
@@ -28,7 +28,7 @@ import {
28
28
  saveVerification,
29
29
  updateDecisionStatus,
30
30
  updateTodoStatus
31
- } from "./chunk-2NBESJTH.js";
31
+ } from "./chunk-GDNAWLHF.js";
32
32
  import "./chunk-E2KFPEBT.js";
33
33
  export {
34
34
  addContextNote,
@@ -14,8 +14,8 @@ import {
14
14
  searchGraph,
15
15
  traceFlow,
16
16
  upsertNode
17
- } from "./chunk-LLAPHR5W.js";
18
- import "./chunk-2NBESJTH.js";
17
+ } from "./chunk-K64NSHBR.js";
18
+ import "./chunk-GDNAWLHF.js";
19
19
  import "./chunk-E2KFPEBT.js";
20
20
  export {
21
21
  addEdge,
@@ -4,8 +4,8 @@ import {
4
4
  recordDecision,
5
5
  scoreMemoryRelevance,
6
6
  shouldRecordDecision
7
- } from "./chunk-6QX4LBHV.js";
8
- import "./chunk-PH4JKL6T.js";
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
1
  import {
2
2
  recordDecisionLog
3
- } from "./chunk-2NBESJTH.js";
3
+ } from "./chunk-GDNAWLHF.js";
4
4
  import {
5
5
  recordDecision
6
- } from "./chunk-6QX4LBHV.js";
7
- import "./chunk-PH4JKL6T.js";
6
+ } from "./chunk-X5TPBDKO.js";
7
+ import "./chunk-BI7KD3SG.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
- let changedFiles = "";
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({
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  getLatestVerifications,
3
3
  saveVerification
4
- } from "./chunk-2NBESJTH.js";
4
+ } from "./chunk-GDNAWLHF.js";
5
5
  import {
6
6
  sessionMemory
7
- } from "./chunk-PH4JKL6T.js";
7
+ } from "./chunk-BI7KD3SG.js";
8
8
  import "./chunk-E2KFPEBT.js";
9
9
 
10
10
  // src/engine/kumaVerifier.ts
@@ -2,10 +2,10 @@ import {
2
2
  getGitDiffStat,
3
3
  getSessionStats,
4
4
  getUnresolvedCount
5
- } from "./chunk-ZIFLGJ5R.js";
5
+ } from "./chunk-FOQQ2CSL.js";
6
6
  import {
7
7
  sessionMemory
8
- } from "./chunk-PH4JKL6T.js";
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-FQ3YKJ6K.js");
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-FQ3YKJ6K.js");
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-FQ3YKJ6K.js");
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 {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionMemory
3
- } from "./chunk-PH4JKL6T.js";
3
+ } from "./chunk-BI7KD3SG.js";
4
4
  export {
5
5
  sessionMemory
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/kuma",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
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",