@plumpslabs/kuma 2.3.28 → 2.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +835 -406
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1181,8 +1181,8 @@ async function runDoctor() {
|
|
|
1181
1181
|
} catch {
|
|
1182
1182
|
}
|
|
1183
1183
|
try {
|
|
1184
|
-
const { execSync:
|
|
1185
|
-
const psOutput =
|
|
1184
|
+
const { execSync: execSync8 } = await import("child_process");
|
|
1185
|
+
const psOutput = execSync8(
|
|
1186
1186
|
`ps -eo pid,ppid,command | grep -E "(jest|pnpm test|npm test|yarn test)" | grep -v grep | head -10`,
|
|
1187
1187
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
1188
1188
|
).trim();
|
|
@@ -3169,27 +3169,27 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
3169
3169
|
if (results.length >= limit || depth > MAX_WALK_DEPTH) return;
|
|
3170
3170
|
let entries = [];
|
|
3171
3171
|
try {
|
|
3172
|
-
entries =
|
|
3172
|
+
entries = fs27.readdirSync(dir);
|
|
3173
3173
|
} catch {
|
|
3174
3174
|
return;
|
|
3175
3175
|
}
|
|
3176
3176
|
for (const entry of entries) {
|
|
3177
3177
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "build") continue;
|
|
3178
|
-
const full =
|
|
3178
|
+
const full = path27.join(dir, entry);
|
|
3179
3179
|
try {
|
|
3180
|
-
const stat =
|
|
3180
|
+
const stat = fs27.statSync(full);
|
|
3181
3181
|
if (stat.isDirectory()) {
|
|
3182
3182
|
walk2(full, depth + 1);
|
|
3183
3183
|
} else if (entry.toLowerCase().includes(query.toLowerCase())) {
|
|
3184
|
-
results.push(
|
|
3184
|
+
results.push(path27.relative(root, full));
|
|
3185
3185
|
}
|
|
3186
3186
|
} catch {
|
|
3187
3187
|
}
|
|
3188
3188
|
}
|
|
3189
3189
|
};
|
|
3190
3190
|
var walk = walk2;
|
|
3191
|
-
const
|
|
3192
|
-
const
|
|
3191
|
+
const fs27 = await import("fs");
|
|
3192
|
+
const path27 = await import("path");
|
|
3193
3193
|
const root = process.cwd();
|
|
3194
3194
|
const results = [];
|
|
3195
3195
|
const MAX_WALK_DEPTH = 8;
|
|
@@ -3329,9 +3329,9 @@ async function analyzeImpact(target) {
|
|
|
3329
3329
|
}
|
|
3330
3330
|
async function codebaseImpactFallback(target) {
|
|
3331
3331
|
try {
|
|
3332
|
-
const { execSync:
|
|
3332
|
+
const { execSync: execSync8 } = await import("child_process");
|
|
3333
3333
|
const root = process.cwd();
|
|
3334
|
-
const grepResult =
|
|
3334
|
+
const grepResult = execSync8(
|
|
3335
3335
|
`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`,
|
|
3336
3336
|
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
3337
3337
|
).trim();
|
|
@@ -5782,8 +5782,11 @@ var init_kumaProgressiveContext = __esm({
|
|
|
5782
5782
|
// src/engine/kumaSearch.ts
|
|
5783
5783
|
var kumaSearch_exports = {};
|
|
5784
5784
|
__export(kumaSearch_exports, {
|
|
5785
|
+
buildGraphConnectivity: () => buildGraphConnectivity,
|
|
5785
5786
|
buildSearchVectors: () => buildSearchVectors,
|
|
5787
|
+
clearConnectivityCache: () => clearConnectivityCache,
|
|
5786
5788
|
clearSearchCache: () => clearSearchCache,
|
|
5789
|
+
enhancedHybridSearch: () => enhancedHybridSearch,
|
|
5787
5790
|
expandQueryTerms: () => expandQueryTerms,
|
|
5788
5791
|
formatHybridResults: () => formatHybridResults,
|
|
5789
5792
|
hybridSearch: () => hybridSearch
|
|
@@ -6050,10 +6053,63 @@ function formatHybridResults(query, results) {
|
|
|
6050
6053
|
lines.push("Hybrid search combines keyword matching with semantic synonym expansion.");
|
|
6051
6054
|
return lines.join("\n");
|
|
6052
6055
|
}
|
|
6056
|
+
async function buildGraphConnectivity() {
|
|
6057
|
+
if (_connectivityCache) return _connectivityCache;
|
|
6058
|
+
const nodeEdges = /* @__PURE__ */ new Map();
|
|
6059
|
+
let totalEdges = 0;
|
|
6060
|
+
try {
|
|
6061
|
+
const db = await getDb();
|
|
6062
|
+
const stmt = db.prepare("SELECT source_id, target_id FROM edges LIMIT 2000");
|
|
6063
|
+
while (stmt.step()) {
|
|
6064
|
+
const row = stmt.getAsObject();
|
|
6065
|
+
const src = row.source_id || "";
|
|
6066
|
+
const tgt = row.target_id || "";
|
|
6067
|
+
if (src) nodeEdges.set(src, (nodeEdges.get(src) || 0) + 1);
|
|
6068
|
+
if (tgt) nodeEdges.set(tgt, (nodeEdges.get(tgt) || 0) + 1);
|
|
6069
|
+
totalEdges++;
|
|
6070
|
+
}
|
|
6071
|
+
stmt.free();
|
|
6072
|
+
} catch {
|
|
6073
|
+
}
|
|
6074
|
+
_connectivityCache = { nodeEdges, totalEdges };
|
|
6075
|
+
setTimeout(() => {
|
|
6076
|
+
_connectivityCache = null;
|
|
6077
|
+
}, CONNECTIVITY_CACHE_TTL);
|
|
6078
|
+
return _connectivityCache;
|
|
6079
|
+
}
|
|
6080
|
+
function connectivityScore(nodeId2, connectivity) {
|
|
6081
|
+
if (connectivity.totalEdges === 0) return 0;
|
|
6082
|
+
const edges = connectivity.nodeEdges.get(nodeId2) || 0;
|
|
6083
|
+
return Math.min(1, Math.log(1 + edges) / Math.log(1 + 20));
|
|
6084
|
+
}
|
|
6085
|
+
function clearConnectivityCache() {
|
|
6086
|
+
_connectivityCache = null;
|
|
6087
|
+
}
|
|
6088
|
+
async function enhancedHybridSearch(query, limit = 10) {
|
|
6089
|
+
const basicResults = await hybridSearch(query, limit * 2);
|
|
6090
|
+
const connectivity = await buildGraphConnectivity();
|
|
6091
|
+
for (const result of basicResults) {
|
|
6092
|
+
try {
|
|
6093
|
+
const db = await getDb();
|
|
6094
|
+
const stmt = db.prepare("SELECT id FROM nodes WHERE name LIKE ? LIMIT 1");
|
|
6095
|
+
stmt.bind([`%${result.source}%`]);
|
|
6096
|
+
if (stmt.step()) {
|
|
6097
|
+
const row = stmt.getAsObject();
|
|
6098
|
+
const nodeId2 = row.id || "";
|
|
6099
|
+
const connScore = connectivityScore(nodeId2, connectivity);
|
|
6100
|
+
result.score = Math.min(100, Math.round(result.score * 0.6 + connScore * 40));
|
|
6101
|
+
}
|
|
6102
|
+
stmt.free();
|
|
6103
|
+
} catch {
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
return basicResults.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
6107
|
+
}
|
|
6053
6108
|
function clearSearchCache() {
|
|
6054
6109
|
_vectorCache = null;
|
|
6110
|
+
clearConnectivityCache();
|
|
6055
6111
|
}
|
|
6056
|
-
var SYNONYM_MAP, _vectorCache, VECTOR_CACHE_TTL;
|
|
6112
|
+
var SYNONYM_MAP, _vectorCache, VECTOR_CACHE_TTL, _connectivityCache, CONNECTIVITY_CACHE_TTL;
|
|
6057
6113
|
var init_kumaSearch = __esm({
|
|
6058
6114
|
"src/engine/kumaSearch.ts"() {
|
|
6059
6115
|
"use strict";
|
|
@@ -6100,6 +6156,8 @@ var init_kumaSearch = __esm({
|
|
|
6100
6156
|
};
|
|
6101
6157
|
_vectorCache = null;
|
|
6102
6158
|
VECTOR_CACHE_TTL = 3e5;
|
|
6159
|
+
_connectivityCache = null;
|
|
6160
|
+
CONNECTIVITY_CACHE_TTL = 3e5;
|
|
6103
6161
|
}
|
|
6104
6162
|
});
|
|
6105
6163
|
|
|
@@ -6441,6 +6499,236 @@ var init_kumaGotchas = __esm({
|
|
|
6441
6499
|
}
|
|
6442
6500
|
});
|
|
6443
6501
|
|
|
6502
|
+
// src/engine/kumaGitHarvester.ts
|
|
6503
|
+
var kumaGitHarvester_exports = {};
|
|
6504
|
+
__export(kumaGitHarvester_exports, {
|
|
6505
|
+
detectSignificantCommits: () => detectSignificantCommits,
|
|
6506
|
+
getRecentCommits: () => getRecentCommits,
|
|
6507
|
+
harvestGitHistory: () => harvestGitHistory,
|
|
6508
|
+
installGitHook: () => installGitHook
|
|
6509
|
+
});
|
|
6510
|
+
import { execSync as execSync5 } from "child_process";
|
|
6511
|
+
import fs14 from "fs";
|
|
6512
|
+
import path14 from "path";
|
|
6513
|
+
function getRecentCommits(count = 5) {
|
|
6514
|
+
const root = getProjectRoot();
|
|
6515
|
+
try {
|
|
6516
|
+
const format = JSON.stringify({
|
|
6517
|
+
hash: "%H",
|
|
6518
|
+
message: "%s",
|
|
6519
|
+
author: "%an",
|
|
6520
|
+
date: "%ai"
|
|
6521
|
+
});
|
|
6522
|
+
const log = execSync5(
|
|
6523
|
+
`git log -n ${count} --pretty=format:'${format.replace(/'/g, "'\\''")}' --name-only`,
|
|
6524
|
+
{ cwd: root, encoding: "utf-8", timeout: 5e3 }
|
|
6525
|
+
);
|
|
6526
|
+
const commits = [];
|
|
6527
|
+
const entries = log.split("\n\n").filter(Boolean);
|
|
6528
|
+
for (const entry of entries) {
|
|
6529
|
+
const lines = entry.split("\n");
|
|
6530
|
+
if (lines.length < 1) continue;
|
|
6531
|
+
try {
|
|
6532
|
+
const meta = JSON.parse(lines[0]);
|
|
6533
|
+
const files = lines.slice(1).filter((f) => f.trim() && !f.startsWith("commit "));
|
|
6534
|
+
let insertions = 0;
|
|
6535
|
+
let deletions = 0;
|
|
6536
|
+
try {
|
|
6537
|
+
const stats = execSync5(
|
|
6538
|
+
`git diff --shortstat ${meta.hash}^..${meta.hash}`,
|
|
6539
|
+
{ cwd: root, encoding: "utf-8", timeout: 3e3 }
|
|
6540
|
+
);
|
|
6541
|
+
const insMatch = stats.match(/(\d+) insertion/);
|
|
6542
|
+
const delMatch = stats.match(/(\d+) deletion/);
|
|
6543
|
+
if (insMatch) insertions = parseInt(insMatch[1], 10);
|
|
6544
|
+
if (delMatch) deletions = parseInt(delMatch[1], 10);
|
|
6545
|
+
} catch {
|
|
6546
|
+
}
|
|
6547
|
+
commits.push({
|
|
6548
|
+
hash: meta.hash?.substring(0, 8) || "",
|
|
6549
|
+
message: meta.message || "",
|
|
6550
|
+
author: meta.author || "",
|
|
6551
|
+
date: meta.date || "",
|
|
6552
|
+
files,
|
|
6553
|
+
insertions,
|
|
6554
|
+
deletions
|
|
6555
|
+
});
|
|
6556
|
+
} catch {
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
return commits;
|
|
6560
|
+
} catch {
|
|
6561
|
+
return [];
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6564
|
+
function detectSignificantCommits(commits) {
|
|
6565
|
+
const decisionKeywords = [
|
|
6566
|
+
"refactor",
|
|
6567
|
+
"migrate",
|
|
6568
|
+
"switch to",
|
|
6569
|
+
"replace",
|
|
6570
|
+
"use .* instead",
|
|
6571
|
+
"remove",
|
|
6572
|
+
"drop",
|
|
6573
|
+
"deprecate",
|
|
6574
|
+
"upgrade",
|
|
6575
|
+
"update to",
|
|
6576
|
+
"architect",
|
|
6577
|
+
"design",
|
|
6578
|
+
"implement",
|
|
6579
|
+
"add feature"
|
|
6580
|
+
];
|
|
6581
|
+
const gotchaKeywords = [
|
|
6582
|
+
"fix",
|
|
6583
|
+
"bug",
|
|
6584
|
+
"hotfix",
|
|
6585
|
+
"patch",
|
|
6586
|
+
"workaround",
|
|
6587
|
+
"revert",
|
|
6588
|
+
"broken",
|
|
6589
|
+
"issue",
|
|
6590
|
+
"error",
|
|
6591
|
+
"crash",
|
|
6592
|
+
"race condition",
|
|
6593
|
+
"memory leak",
|
|
6594
|
+
"performance"
|
|
6595
|
+
];
|
|
6596
|
+
const decisions = [];
|
|
6597
|
+
const gotchas = [];
|
|
6598
|
+
const regular = [];
|
|
6599
|
+
for (const commit of commits) {
|
|
6600
|
+
const msg = commit.message.toLowerCase();
|
|
6601
|
+
const isLargeChange = commit.files.length > 3 || commit.insertions + commit.deletions > 100;
|
|
6602
|
+
const isDecision = decisionKeywords.some((kw) => new RegExp(kw, "i").test(msg)) || isLargeChange;
|
|
6603
|
+
const isGotcha = gotchaKeywords.some((kw) => new RegExp(kw, "i").test(msg));
|
|
6604
|
+
if (isDecision && !isGotcha) {
|
|
6605
|
+
decisions.push(commit);
|
|
6606
|
+
} else if (isGotcha) {
|
|
6607
|
+
gotchas.push(commit);
|
|
6608
|
+
} else {
|
|
6609
|
+
regular.push(commit);
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
return { decisions, gotchas, regular };
|
|
6613
|
+
}
|
|
6614
|
+
async function harvestGitHistory(options) {
|
|
6615
|
+
const count = options?.commitCount || 10;
|
|
6616
|
+
const dryRun = options?.dryRun || false;
|
|
6617
|
+
const commits = getRecentCommits(count);
|
|
6618
|
+
if (commits.length === 0) {
|
|
6619
|
+
return "\u{1F4ED} No git commits found. Is this a git repository?";
|
|
6620
|
+
}
|
|
6621
|
+
const { decisions, gotchas } = detectSignificantCommits(commits);
|
|
6622
|
+
const lines = [
|
|
6623
|
+
"\u{1F419} **Git Auto-Harvester**",
|
|
6624
|
+
"\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",
|
|
6625
|
+
"",
|
|
6626
|
+
`\u{1F4CA} Scanned ${commits.length} commit(s)`,
|
|
6627
|
+
`\u2696\uFE0F ${decisions.length} significant change(s) \u2192 decisions`,
|
|
6628
|
+
`\u26A0\uFE0F ${gotchas.length} fix/revert(s) \u2192 gotchas`,
|
|
6629
|
+
""
|
|
6630
|
+
];
|
|
6631
|
+
if (dryRun) {
|
|
6632
|
+
lines.push("**Dry Run \u2014 no nodes created:**");
|
|
6633
|
+
lines.push("");
|
|
6634
|
+
if (decisions.length > 0) {
|
|
6635
|
+
lines.push("**Potential Decisions:**");
|
|
6636
|
+
for (const c of decisions) {
|
|
6637
|
+
lines.push(` \u2696\uFE0F ${c.hash} \u2014 ${c.message}`);
|
|
6638
|
+
lines.push(` Files: ${c.files.length}, +${c.insertions}/-${c.deletions}`);
|
|
6639
|
+
}
|
|
6640
|
+
lines.push("");
|
|
6641
|
+
}
|
|
6642
|
+
if (gotchas.length > 0) {
|
|
6643
|
+
lines.push("**Potential Gotchas:**");
|
|
6644
|
+
for (const c of gotchas) {
|
|
6645
|
+
lines.push(` \u26A0\uFE0F ${c.hash} \u2014 ${c.message}`);
|
|
6646
|
+
lines.push(` Files: ${c.files.join(", ")}`);
|
|
6647
|
+
}
|
|
6648
|
+
lines.push("");
|
|
6649
|
+
}
|
|
6650
|
+
lines.push("\u{1F4A1} Run without dryRun to create nodes.");
|
|
6651
|
+
return lines.join("\n");
|
|
6652
|
+
}
|
|
6653
|
+
let decisionCount = 0;
|
|
6654
|
+
for (const commit of decisions) {
|
|
6655
|
+
try {
|
|
6656
|
+
const { recordDecision: recordDecision2 } = await Promise.resolve().then(() => (init_kumaMemory(), kumaMemory_exports));
|
|
6657
|
+
await recordDecision2({
|
|
6658
|
+
title: commit.message,
|
|
6659
|
+
context: `Auto-harvested from git commit ${commit.hash}`,
|
|
6660
|
+
options: [],
|
|
6661
|
+
rationale: `Files changed: ${commit.files.slice(0, 5).join(", ")}${commit.files.length > 5 ? ` +${commit.files.length - 5} more` : ""}`,
|
|
6662
|
+
outcome: "implemented",
|
|
6663
|
+
timestamp: new Date(commit.date).toISOString()
|
|
6664
|
+
});
|
|
6665
|
+
decisionCount++;
|
|
6666
|
+
} catch {
|
|
6667
|
+
}
|
|
6668
|
+
}
|
|
6669
|
+
let gotchaCount = 0;
|
|
6670
|
+
for (const commit of gotchas) {
|
|
6671
|
+
try {
|
|
6672
|
+
const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
6673
|
+
const primaryFile = commit.files[0] || "unknown";
|
|
6674
|
+
await addGotcha2({
|
|
6675
|
+
filePath: primaryFile,
|
|
6676
|
+
description: `Git commit ${commit.hash}: ${commit.message}`,
|
|
6677
|
+
severity: commit.message.toLowerCase().includes("revert") ? "high" : "medium",
|
|
6678
|
+
workaround: `See commit ${commit.hash} for details`
|
|
6679
|
+
});
|
|
6680
|
+
gotchaCount++;
|
|
6681
|
+
} catch {
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
lines.push(`\u2705 Harvested: ${decisionCount} decision(s), ${gotchaCount} gotcha(s)`);
|
|
6685
|
+
if (decisionCount + gotchaCount === 0) {
|
|
6686
|
+
lines.push("\u{1F4ED} No significant commits to harvest.");
|
|
6687
|
+
}
|
|
6688
|
+
return lines.join("\n");
|
|
6689
|
+
}
|
|
6690
|
+
function installGitHook() {
|
|
6691
|
+
const root = getProjectRoot();
|
|
6692
|
+
const hooksDir = path14.join(root, ".git", "hooks");
|
|
6693
|
+
if (!fs14.existsSync(hooksDir)) {
|
|
6694
|
+
return "\u26A0\uFE0F Not a git repository \u2014 no .git/hooks/ directory found.";
|
|
6695
|
+
}
|
|
6696
|
+
const hookPath = path14.join(hooksDir, "post-commit");
|
|
6697
|
+
const hookContent = `#!/bin/bash
|
|
6698
|
+
# Kuma auto-harvester hook (post-commit)
|
|
6699
|
+
# Automatically extracts decisions and gotchas from commits
|
|
6700
|
+
if command -v npx &> /dev/null; then
|
|
6701
|
+
npx -y @plumpslabs/kuma --hook post-commit 2>/dev/null || true
|
|
6702
|
+
fi
|
|
6703
|
+
`;
|
|
6704
|
+
if (fs14.existsSync(hookPath)) {
|
|
6705
|
+
const existing = fs14.readFileSync(hookPath, "utf-8");
|
|
6706
|
+
if (existing.includes("kuma")) {
|
|
6707
|
+
return "\u2705 Kuma git hook already installed.";
|
|
6708
|
+
}
|
|
6709
|
+
const backupPath = hookPath + ".backup." + Date.now();
|
|
6710
|
+
fs14.copyFileSync(hookPath, backupPath);
|
|
6711
|
+
fs14.writeFileSync(hookPath, hookContent, "utf-8");
|
|
6712
|
+
try {
|
|
6713
|
+
fs14.chmodSync(hookPath, 493);
|
|
6714
|
+
} catch {
|
|
6715
|
+
}
|
|
6716
|
+
return `\u2705 Installed kuma post-commit hook (existing hook backed up to ${path14.basename(backupPath)})`;
|
|
6717
|
+
}
|
|
6718
|
+
fs14.writeFileSync(hookPath, hookContent, "utf-8");
|
|
6719
|
+
try {
|
|
6720
|
+
fs14.chmodSync(hookPath, 493);
|
|
6721
|
+
} catch {
|
|
6722
|
+
}
|
|
6723
|
+
return "\u2705 Installed kuma post-commit hook.";
|
|
6724
|
+
}
|
|
6725
|
+
var init_kumaGitHarvester = __esm({
|
|
6726
|
+
"src/engine/kumaGitHarvester.ts"() {
|
|
6727
|
+
"use strict";
|
|
6728
|
+
init_pathValidator();
|
|
6729
|
+
}
|
|
6730
|
+
});
|
|
6731
|
+
|
|
6444
6732
|
// src/engine/safetyAudit.ts
|
|
6445
6733
|
var safetyAudit_exports = {};
|
|
6446
6734
|
__export(safetyAudit_exports, {
|
|
@@ -6610,19 +6898,19 @@ __export(kumaPolicyEngine_exports, {
|
|
|
6610
6898
|
processOverride: () => processOverride,
|
|
6611
6899
|
savePolicyConfig: () => savePolicyConfig
|
|
6612
6900
|
});
|
|
6613
|
-
import
|
|
6614
|
-
import
|
|
6901
|
+
import fs20 from "fs";
|
|
6902
|
+
import path20 from "path";
|
|
6615
6903
|
function loadPolicyConfig() {
|
|
6616
6904
|
try {
|
|
6617
6905
|
const root = getProjectRoot();
|
|
6618
|
-
const policyPath =
|
|
6619
|
-
if (
|
|
6620
|
-
const content =
|
|
6906
|
+
const policyPath = path20.join(root, ".kuma", "POLICY.json");
|
|
6907
|
+
if (fs20.existsSync(policyPath)) {
|
|
6908
|
+
const content = fs20.readFileSync(policyPath, "utf-8");
|
|
6621
6909
|
const config = JSON.parse(content);
|
|
6622
6910
|
return { ...DEFAULT_POLICY_CONFIG, ...config };
|
|
6623
6911
|
}
|
|
6624
|
-
const ymlPath =
|
|
6625
|
-
if (
|
|
6912
|
+
const ymlPath = path20.join(root, ".kuma", "policy.yml");
|
|
6913
|
+
if (fs20.existsSync(ymlPath)) {
|
|
6626
6914
|
console.error("[PolicyEngine] Found legacy policy.yml \u2014 consider migrating to POLICY.json");
|
|
6627
6915
|
}
|
|
6628
6916
|
} catch (err) {
|
|
@@ -6633,10 +6921,10 @@ function loadPolicyConfig() {
|
|
|
6633
6921
|
function savePolicyConfig(config) {
|
|
6634
6922
|
try {
|
|
6635
6923
|
const root = getProjectRoot();
|
|
6636
|
-
const policyPath =
|
|
6637
|
-
const dir =
|
|
6638
|
-
if (!
|
|
6639
|
-
|
|
6924
|
+
const policyPath = path20.join(root, ".kuma", "POLICY.json");
|
|
6925
|
+
const dir = path20.dirname(policyPath);
|
|
6926
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
6927
|
+
fs20.writeFileSync(policyPath, JSON.stringify(config, null, 2), "utf-8");
|
|
6640
6928
|
return `\u2705 Policy config saved to .kuma/POLICY.json (${config.rules.length} rules)`;
|
|
6641
6929
|
} catch (err) {
|
|
6642
6930
|
return `\u274C Failed to save policy: ${err}`;
|
|
@@ -6921,14 +7209,14 @@ __export(kumaAstValidator_exports, {
|
|
|
6921
7209
|
validateDiff: () => validateDiff,
|
|
6922
7210
|
validateFile: () => validateFile
|
|
6923
7211
|
});
|
|
6924
|
-
import
|
|
6925
|
-
import
|
|
7212
|
+
import fs21 from "fs";
|
|
7213
|
+
import path21 from "path";
|
|
6926
7214
|
function loadImportWhitelist() {
|
|
6927
7215
|
try {
|
|
6928
7216
|
const root = getProjectRoot();
|
|
6929
|
-
const whitelistPath =
|
|
6930
|
-
if (
|
|
6931
|
-
return JSON.parse(
|
|
7217
|
+
const whitelistPath = path21.join(root, ".kuma", "import-whitelist.json");
|
|
7218
|
+
if (fs21.existsSync(whitelistPath)) {
|
|
7219
|
+
return JSON.parse(fs21.readFileSync(whitelistPath, "utf-8"));
|
|
6932
7220
|
}
|
|
6933
7221
|
} catch {
|
|
6934
7222
|
}
|
|
@@ -7002,9 +7290,9 @@ function validateCodeContent(code, filePath) {
|
|
|
7002
7290
|
function validateFile(filePath) {
|
|
7003
7291
|
try {
|
|
7004
7292
|
const root = getProjectRoot();
|
|
7005
|
-
const fullPath =
|
|
7006
|
-
if (!
|
|
7007
|
-
const content =
|
|
7293
|
+
const fullPath = path21.resolve(root, filePath);
|
|
7294
|
+
if (!fs21.existsSync(fullPath)) return [];
|
|
7295
|
+
const content = fs21.readFileSync(fullPath, "utf-8");
|
|
7008
7296
|
return validateCodeContent(content, filePath);
|
|
7009
7297
|
} catch {
|
|
7010
7298
|
return [];
|
|
@@ -7075,16 +7363,16 @@ __export(kumaCheckpoint_exports, {
|
|
|
7075
7363
|
pruneCheckpoints: () => pruneCheckpoints,
|
|
7076
7364
|
rollbackToCheckpoint: () => rollbackToCheckpoint
|
|
7077
7365
|
});
|
|
7078
|
-
import
|
|
7079
|
-
import
|
|
7366
|
+
import fs22 from "fs";
|
|
7367
|
+
import path22 from "path";
|
|
7080
7368
|
async function createCheckpoint(label, description) {
|
|
7081
7369
|
try {
|
|
7082
7370
|
const root = getProjectRoot();
|
|
7083
|
-
const cpDir =
|
|
7084
|
-
if (
|
|
7371
|
+
const cpDir = path22.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
7372
|
+
if (fs22.existsSync(cpDir)) {
|
|
7085
7373
|
return `\u26A0\uFE0F Checkpoint "${label}" already exists. Use a different label or remove it first.`;
|
|
7086
7374
|
}
|
|
7087
|
-
|
|
7375
|
+
fs22.mkdirSync(cpDir, { recursive: true });
|
|
7088
7376
|
const db = await getDb();
|
|
7089
7377
|
const filesStmt = db.prepare(
|
|
7090
7378
|
"SELECT DISTINCT file_path FROM change_log ORDER BY id DESC LIMIT 100"
|
|
@@ -7093,19 +7381,19 @@ async function createCheckpoint(label, description) {
|
|
|
7093
7381
|
while (filesStmt.step()) {
|
|
7094
7382
|
const row = filesStmt.getAsObject();
|
|
7095
7383
|
const fp = row.file_path;
|
|
7096
|
-
const fullPath =
|
|
7097
|
-
if (
|
|
7098
|
-
const content =
|
|
7384
|
+
const fullPath = path22.resolve(root, fp);
|
|
7385
|
+
if (fs22.existsSync(fullPath)) {
|
|
7386
|
+
const content = fs22.readFileSync(fullPath, "utf-8");
|
|
7099
7387
|
const hash = simpleHash2(content);
|
|
7100
|
-
const fileDir =
|
|
7101
|
-
if (!
|
|
7102
|
-
|
|
7388
|
+
const fileDir = path22.dirname(path22.join(cpDir, "files", fp));
|
|
7389
|
+
if (!fs22.existsSync(fileDir)) fs22.mkdirSync(fileDir, { recursive: true });
|
|
7390
|
+
fs22.writeFileSync(path22.join(cpDir, "files", fp), content, "utf-8");
|
|
7103
7391
|
files.push({ path: fp, hash });
|
|
7104
7392
|
}
|
|
7105
7393
|
}
|
|
7106
7394
|
filesStmt.free();
|
|
7107
7395
|
const dbData = db.export();
|
|
7108
|
-
|
|
7396
|
+
fs22.writeFileSync(path22.join(cpDir, "kuma.db"), Buffer.from(dbData));
|
|
7109
7397
|
const manifest = {
|
|
7110
7398
|
label,
|
|
7111
7399
|
timestamp: Date.now(),
|
|
@@ -7113,8 +7401,8 @@ async function createCheckpoint(label, description) {
|
|
|
7113
7401
|
dbSnapshot: true,
|
|
7114
7402
|
description
|
|
7115
7403
|
};
|
|
7116
|
-
|
|
7117
|
-
|
|
7404
|
+
fs22.writeFileSync(
|
|
7405
|
+
path22.join(cpDir, "manifest.json"),
|
|
7118
7406
|
JSON.stringify(manifest, null, 2),
|
|
7119
7407
|
"utf-8"
|
|
7120
7408
|
);
|
|
@@ -7140,37 +7428,37 @@ async function createCheckpoint(label, description) {
|
|
|
7140
7428
|
async function rollbackToCheckpoint(label) {
|
|
7141
7429
|
try {
|
|
7142
7430
|
const root = getProjectRoot();
|
|
7143
|
-
const cpDir =
|
|
7144
|
-
const manifestPath =
|
|
7145
|
-
if (!
|
|
7431
|
+
const cpDir = path22.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
7432
|
+
const manifestPath = path22.join(cpDir, "manifest.json");
|
|
7433
|
+
if (!fs22.existsSync(manifestPath)) {
|
|
7146
7434
|
return `\u274C Checkpoint "${label}" not found. Use kuma_safety({ action: 'checkpoint_list' }) to see available checkpoints.`;
|
|
7147
7435
|
}
|
|
7148
7436
|
const manifest = JSON.parse(
|
|
7149
|
-
|
|
7437
|
+
fs22.readFileSync(manifestPath, "utf-8")
|
|
7150
7438
|
);
|
|
7151
7439
|
let restored = 0;
|
|
7152
7440
|
let failed = 0;
|
|
7153
7441
|
for (const f of manifest.files) {
|
|
7154
|
-
const snapshotPath =
|
|
7155
|
-
if (
|
|
7442
|
+
const snapshotPath = path22.join(cpDir, "files", f.path);
|
|
7443
|
+
if (fs22.existsSync(snapshotPath)) {
|
|
7156
7444
|
try {
|
|
7157
|
-
const content =
|
|
7158
|
-
const targetPath =
|
|
7159
|
-
const targetDir =
|
|
7160
|
-
if (!
|
|
7161
|
-
|
|
7445
|
+
const content = fs22.readFileSync(snapshotPath, "utf-8");
|
|
7446
|
+
const targetPath = path22.resolve(root, f.path);
|
|
7447
|
+
const targetDir = path22.dirname(targetPath);
|
|
7448
|
+
if (!fs22.existsSync(targetDir)) fs22.mkdirSync(targetDir, { recursive: true });
|
|
7449
|
+
fs22.writeFileSync(targetPath, content, "utf-8");
|
|
7162
7450
|
restored++;
|
|
7163
7451
|
} catch {
|
|
7164
7452
|
failed++;
|
|
7165
7453
|
}
|
|
7166
7454
|
}
|
|
7167
7455
|
}
|
|
7168
|
-
const dbSnapshotPath =
|
|
7169
|
-
if (
|
|
7170
|
-
const kumaDir =
|
|
7171
|
-
const dbPath =
|
|
7172
|
-
const snapshotData =
|
|
7173
|
-
|
|
7456
|
+
const dbSnapshotPath = path22.join(cpDir, "kuma.db");
|
|
7457
|
+
if (fs22.existsSync(dbSnapshotPath)) {
|
|
7458
|
+
const kumaDir = path22.join(root, ".kuma");
|
|
7459
|
+
const dbPath = path22.join(kumaDir, "kuma.db");
|
|
7460
|
+
const snapshotData = fs22.readFileSync(dbSnapshotPath);
|
|
7461
|
+
fs22.writeFileSync(dbPath, snapshotData);
|
|
7174
7462
|
resetDbInstance();
|
|
7175
7463
|
}
|
|
7176
7464
|
sessionMemory.recordToolCall("kuma_checkpoint_rollback", {
|
|
@@ -7195,18 +7483,18 @@ async function rollbackToCheckpoint(label) {
|
|
|
7195
7483
|
function listCheckpoints() {
|
|
7196
7484
|
try {
|
|
7197
7485
|
const root = getProjectRoot();
|
|
7198
|
-
const cpDir =
|
|
7199
|
-
if (!
|
|
7486
|
+
const cpDir = path22.join(root, CHECKPOINT_DIR);
|
|
7487
|
+
if (!fs22.existsSync(cpDir)) {
|
|
7200
7488
|
return "\u{1F4ED} No checkpoints found. Use kuma_safety({ action: 'checkpoint', label: 'pre-feature-x' }) to create one.";
|
|
7201
7489
|
}
|
|
7202
|
-
const entries =
|
|
7490
|
+
const entries = fs22.readdirSync(cpDir);
|
|
7203
7491
|
const checkpoints = [];
|
|
7204
7492
|
for (const entry of entries) {
|
|
7205
|
-
const manifestPath =
|
|
7206
|
-
if (
|
|
7493
|
+
const manifestPath = path22.join(cpDir, entry, "manifest.json");
|
|
7494
|
+
if (fs22.existsSync(manifestPath)) {
|
|
7207
7495
|
try {
|
|
7208
7496
|
const manifest = JSON.parse(
|
|
7209
|
-
|
|
7497
|
+
fs22.readFileSync(manifestPath, "utf-8")
|
|
7210
7498
|
);
|
|
7211
7499
|
checkpoints.push({ label: entry, manifest });
|
|
7212
7500
|
} catch {
|
|
@@ -7238,12 +7526,12 @@ function listCheckpoints() {
|
|
|
7238
7526
|
function pruneCheckpoints(keep = 5) {
|
|
7239
7527
|
try {
|
|
7240
7528
|
const root = getProjectRoot();
|
|
7241
|
-
const cpDir =
|
|
7242
|
-
if (!
|
|
7243
|
-
const entries =
|
|
7529
|
+
const cpDir = path22.join(root, CHECKPOINT_DIR);
|
|
7530
|
+
if (!fs22.existsSync(cpDir)) return "\u{1F4ED} No checkpoints to prune.";
|
|
7531
|
+
const entries = fs22.readdirSync(cpDir).map((e) => ({ name: e, time: fs22.statSync(path22.join(cpDir, e)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
7244
7532
|
let removed = 0;
|
|
7245
7533
|
for (let i = keep; i < entries.length; i++) {
|
|
7246
|
-
|
|
7534
|
+
fs22.rmSync(path22.join(cpDir, entries[i].name), { recursive: true, force: true });
|
|
7247
7535
|
removed++;
|
|
7248
7536
|
}
|
|
7249
7537
|
return removed > 0 ? `\u{1F9F9} Pruned **${removed} old checkpoint(s)**. Kept ${Math.min(keep, entries.length)} most recent.` : "\u2705 No checkpoints needed pruning.";
|
|
@@ -7283,18 +7571,18 @@ __export(kumaContractEngine_exports, {
|
|
|
7283
7571
|
loadContracts: () => loadContracts,
|
|
7284
7572
|
runContractChecks: () => runContractChecks
|
|
7285
7573
|
});
|
|
7286
|
-
import
|
|
7287
|
-
import
|
|
7574
|
+
import fs23 from "fs";
|
|
7575
|
+
import path23 from "path";
|
|
7288
7576
|
function contractsDir() {
|
|
7289
|
-
return
|
|
7577
|
+
return path23.join(getProjectRoot(), ".kuma", "contracts");
|
|
7290
7578
|
}
|
|
7291
7579
|
function ensureContractsDir() {
|
|
7292
7580
|
const dir = contractsDir();
|
|
7293
|
-
if (!
|
|
7294
|
-
|
|
7295
|
-
const defaultPath =
|
|
7296
|
-
if (!
|
|
7297
|
-
|
|
7581
|
+
if (!fs23.existsSync(dir)) {
|
|
7582
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
7583
|
+
const defaultPath = path23.join(dir, "default.json");
|
|
7584
|
+
if (!fs23.existsSync(defaultPath)) {
|
|
7585
|
+
fs23.writeFileSync(
|
|
7298
7586
|
defaultPath,
|
|
7299
7587
|
JSON.stringify(DEFAULT_CONTRACTS, null, 2),
|
|
7300
7588
|
"utf-8"
|
|
@@ -7307,10 +7595,10 @@ function loadContracts() {
|
|
|
7307
7595
|
const dir = contractsDir();
|
|
7308
7596
|
const contracts = [];
|
|
7309
7597
|
try {
|
|
7310
|
-
const files =
|
|
7598
|
+
const files = fs23.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
7311
7599
|
for (const file of files) {
|
|
7312
7600
|
try {
|
|
7313
|
-
const content =
|
|
7601
|
+
const content = fs23.readFileSync(path23.join(dir, file), "utf-8");
|
|
7314
7602
|
const config = JSON.parse(content);
|
|
7315
7603
|
contracts.push(config);
|
|
7316
7604
|
} catch (err) {
|
|
@@ -7383,7 +7671,7 @@ function evaluateCondition(condition, contextPath) {
|
|
|
7383
7671
|
switch (condition.type) {
|
|
7384
7672
|
case "file_exists": {
|
|
7385
7673
|
const targetPath = resolveTarget(condition.target, contextPath, root);
|
|
7386
|
-
const exists = targetPath.some((p) =>
|
|
7674
|
+
const exists = targetPath.some((p) => fs23.existsSync(p));
|
|
7387
7675
|
return {
|
|
7388
7676
|
passed: exists,
|
|
7389
7677
|
message: exists ? `\u2705 File exists: ${condition.target}` : `\u274C Required file not found: ${condition.target} (${condition.description})`
|
|
@@ -7391,7 +7679,7 @@ function evaluateCondition(condition, contextPath) {
|
|
|
7391
7679
|
}
|
|
7392
7680
|
case "file_not_exists": {
|
|
7393
7681
|
const targetPath = resolveTarget(condition.target, contextPath, root);
|
|
7394
|
-
const exists = targetPath.some((p) =>
|
|
7682
|
+
const exists = targetPath.some((p) => fs23.existsSync(p));
|
|
7395
7683
|
return {
|
|
7396
7684
|
passed: !exists,
|
|
7397
7685
|
message: !exists ? `\u2705 File does not exist: ${condition.target}` : `\u274C File should not exist: ${condition.target} (${condition.description})`
|
|
@@ -7399,9 +7687,9 @@ function evaluateCondition(condition, contextPath) {
|
|
|
7399
7687
|
}
|
|
7400
7688
|
case "contains": {
|
|
7401
7689
|
if (!contextPath) return { passed: true, message: "\u26A0\uFE0F No file to check" };
|
|
7402
|
-
const fullPath =
|
|
7403
|
-
if (!
|
|
7404
|
-
const content =
|
|
7690
|
+
const fullPath = path23.resolve(root, contextPath);
|
|
7691
|
+
if (!fs23.existsSync(fullPath)) return { passed: true, message: "\u26A0\uFE0F File not found" };
|
|
7692
|
+
const content = fs23.readFileSync(fullPath, "utf-8");
|
|
7405
7693
|
const hasContent = condition.value ? content.includes(condition.value) : false;
|
|
7406
7694
|
return {
|
|
7407
7695
|
passed: hasContent,
|
|
@@ -7413,12 +7701,12 @@ function evaluateCondition(condition, contextPath) {
|
|
|
7413
7701
|
const root2 = getProjectRoot();
|
|
7414
7702
|
const targets = resolveTarget(condition.target, contextPath, root2);
|
|
7415
7703
|
for (const t of targets) {
|
|
7416
|
-
if (
|
|
7417
|
-
const content =
|
|
7704
|
+
if (fs23.existsSync(t)) {
|
|
7705
|
+
const content = fs23.readFileSync(t, "utf-8");
|
|
7418
7706
|
if (condition.value && content.includes(condition.value)) {
|
|
7419
7707
|
return {
|
|
7420
7708
|
passed: false,
|
|
7421
|
-
message: `\u274C Found forbidden pattern "${condition.value}" in ${
|
|
7709
|
+
message: `\u274C Found forbidden pattern "${condition.value}" in ${path23.relative(root2, t)} (${condition.description})`
|
|
7422
7710
|
};
|
|
7423
7711
|
}
|
|
7424
7712
|
}
|
|
@@ -7427,15 +7715,15 @@ function evaluateCondition(condition, contextPath) {
|
|
|
7427
7715
|
}
|
|
7428
7716
|
case "has_test_file": {
|
|
7429
7717
|
if (!contextPath) return { passed: true, message: "\u26A0\uFE0F No file to check" };
|
|
7430
|
-
const basename =
|
|
7431
|
-
const dir =
|
|
7718
|
+
const basename = path23.basename(contextPath, path23.extname(contextPath));
|
|
7719
|
+
const dir = path23.dirname(contextPath);
|
|
7432
7720
|
const possibleTests = [
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7721
|
+
path23.join(dir, `${basename}.test.ts`),
|
|
7722
|
+
path23.join(dir, `${basename}.spec.ts`),
|
|
7723
|
+
path23.join(dir, `__tests__/${basename}.test.ts`),
|
|
7724
|
+
path23.join(dir, `__tests__/${basename}.spec.ts`)
|
|
7437
7725
|
];
|
|
7438
|
-
const hasTest = possibleTests.some((t) =>
|
|
7726
|
+
const hasTest = possibleTests.some((t) => fs23.existsSync(t));
|
|
7439
7727
|
return {
|
|
7440
7728
|
passed: hasTest,
|
|
7441
7729
|
message: hasTest ? `\u2705 Test file exists` : `\u274C No test file found for ${contextPath} (${condition.description})`
|
|
@@ -7459,18 +7747,18 @@ function resolveTarget(pattern, contextPath, root) {
|
|
|
7459
7747
|
const r = root || getProjectRoot();
|
|
7460
7748
|
if (pattern.startsWith("*")) {
|
|
7461
7749
|
if (contextPath) {
|
|
7462
|
-
const dir =
|
|
7750
|
+
const dir = path23.dirname(path23.resolve(r, contextPath));
|
|
7463
7751
|
const suffix = pattern.replace("*", "");
|
|
7464
7752
|
try {
|
|
7465
|
-
const files =
|
|
7466
|
-
return files.filter((f) => f.endsWith(suffix) || f.includes(suffix)).map((f) =>
|
|
7753
|
+
const files = fs23.readdirSync(dir);
|
|
7754
|
+
return files.filter((f) => f.endsWith(suffix) || f.includes(suffix)).map((f) => path23.join(dir, f));
|
|
7467
7755
|
} catch {
|
|
7468
|
-
return [
|
|
7756
|
+
return [path23.join(dir, pattern.replace("*", ""))];
|
|
7469
7757
|
}
|
|
7470
7758
|
}
|
|
7471
7759
|
return [];
|
|
7472
7760
|
}
|
|
7473
|
-
return [
|
|
7761
|
+
return [path23.resolve(r, pattern)];
|
|
7474
7762
|
}
|
|
7475
7763
|
async function runContractChecks(toolName, filePath, phase = "pre") {
|
|
7476
7764
|
const verdicts = phase === "pre" ? evaluatePreConditions(toolName, filePath) : evaluatePostConditions(toolName, filePath);
|
|
@@ -7591,37 +7879,37 @@ __export(kumaVerifier_exports, {
|
|
|
7591
7879
|
getRunningVerificationPid: () => getRunningVerificationPid,
|
|
7592
7880
|
runAutoVerification: () => runAutoVerification
|
|
7593
7881
|
});
|
|
7594
|
-
import
|
|
7595
|
-
import
|
|
7882
|
+
import fs24 from "fs";
|
|
7883
|
+
import path24 from "path";
|
|
7596
7884
|
import { exec } from "child_process";
|
|
7597
7885
|
function getLockDir(root = process.cwd()) {
|
|
7598
|
-
return
|
|
7886
|
+
return path24.resolve(root, LOCK_DIR_NAME);
|
|
7599
7887
|
}
|
|
7600
7888
|
function getPidFile(root = process.cwd()) {
|
|
7601
|
-
return
|
|
7889
|
+
return path24.join(getLockDir(root), "pid");
|
|
7602
7890
|
}
|
|
7603
7891
|
function acquireFileLock(root) {
|
|
7604
7892
|
const lockDir = getLockDir(root);
|
|
7605
7893
|
try {
|
|
7606
|
-
|
|
7607
|
-
|
|
7894
|
+
fs24.mkdirSync(lockDir, { recursive: false });
|
|
7895
|
+
fs24.writeFileSync(getPidFile(root), String(process.pid), "utf-8");
|
|
7608
7896
|
return true;
|
|
7609
7897
|
} catch {
|
|
7610
7898
|
try {
|
|
7611
|
-
if (
|
|
7612
|
-
const pid = parseInt(
|
|
7899
|
+
if (fs24.existsSync(getPidFile(root))) {
|
|
7900
|
+
const pid = parseInt(fs24.readFileSync(getPidFile(root), "utf-8"), 10);
|
|
7613
7901
|
try {
|
|
7614
7902
|
process.kill(pid, 0);
|
|
7615
7903
|
return false;
|
|
7616
7904
|
} catch {
|
|
7617
|
-
|
|
7905
|
+
fs24.rmSync(lockDir, { recursive: true, force: true });
|
|
7618
7906
|
return acquireFileLock(root);
|
|
7619
7907
|
}
|
|
7620
7908
|
}
|
|
7621
7909
|
} catch {
|
|
7622
7910
|
}
|
|
7623
7911
|
try {
|
|
7624
|
-
|
|
7912
|
+
fs24.rmSync(lockDir, { recursive: true, force: true });
|
|
7625
7913
|
return acquireFileLock(root);
|
|
7626
7914
|
} catch {
|
|
7627
7915
|
return false;
|
|
@@ -7630,7 +7918,7 @@ function acquireFileLock(root) {
|
|
|
7630
7918
|
}
|
|
7631
7919
|
function releaseFileLock(root) {
|
|
7632
7920
|
try {
|
|
7633
|
-
|
|
7921
|
+
fs24.rmSync(getLockDir(root), { recursive: true, force: true });
|
|
7634
7922
|
} catch {
|
|
7635
7923
|
}
|
|
7636
7924
|
}
|
|
@@ -7653,36 +7941,36 @@ function getRunningVerificationPid() {
|
|
|
7653
7941
|
return _currentProcess?.pid ?? null;
|
|
7654
7942
|
}
|
|
7655
7943
|
function detectTestRunner(root = process.cwd()) {
|
|
7656
|
-
const pkgPath =
|
|
7657
|
-
if (
|
|
7944
|
+
const pkgPath = path24.join(root, "package.json");
|
|
7945
|
+
if (fs24.existsSync(pkgPath)) {
|
|
7658
7946
|
try {
|
|
7659
|
-
const pkg = JSON.parse(
|
|
7947
|
+
const pkg = JSON.parse(fs24.readFileSync(pkgPath, "utf-8"));
|
|
7660
7948
|
if (pkg.scripts && pkg.scripts.test) {
|
|
7661
|
-
if (
|
|
7662
|
-
if (
|
|
7949
|
+
if (fs24.existsSync(path24.join(root, "pnpm-lock.yaml"))) return { runner: "pnpm", baseCommand: "pnpm test" };
|
|
7950
|
+
if (fs24.existsSync(path24.join(root, "yarn.lock"))) return { runner: "yarn", baseCommand: "yarn test" };
|
|
7663
7951
|
return { runner: "npm", baseCommand: "npm test" };
|
|
7664
7952
|
}
|
|
7665
7953
|
} catch {
|
|
7666
7954
|
}
|
|
7667
7955
|
}
|
|
7668
|
-
if (
|
|
7669
|
-
if (
|
|
7670
|
-
if (
|
|
7671
|
-
if (
|
|
7956
|
+
if (fs24.existsSync(path24.join(root, "pytest.ini")) || fs24.existsSync(path24.join(root, "pyproject.toml"))) return { runner: "pytest", baseCommand: "pytest" };
|
|
7957
|
+
if (fs24.existsSync(path24.join(root, "Cargo.toml"))) return { runner: "cargo", baseCommand: "cargo test" };
|
|
7958
|
+
if (fs24.existsSync(path24.join(root, "go.mod"))) return { runner: "go", baseCommand: "go test ./..." };
|
|
7959
|
+
if (fs24.existsSync(path24.join(root, "Makefile"))) {
|
|
7672
7960
|
try {
|
|
7673
|
-
const makefileContent =
|
|
7961
|
+
const makefileContent = fs24.readFileSync(path24.join(root, "Makefile"), "utf-8");
|
|
7674
7962
|
const hasTestTarget = /^test:/m.test(makefileContent) || /^\s+test:/.test(makefileContent);
|
|
7675
7963
|
if (hasTestTarget) return { runner: "make", baseCommand: "make test" };
|
|
7676
7964
|
} catch {
|
|
7677
7965
|
}
|
|
7678
7966
|
}
|
|
7679
|
-
if (
|
|
7967
|
+
if (fs24.existsSync(path24.join(root, "package.json"))) {
|
|
7680
7968
|
try {
|
|
7681
|
-
const pkg = JSON.parse(
|
|
7969
|
+
const pkg = JSON.parse(fs24.readFileSync(path24.join(root, "package.json"), "utf-8"));
|
|
7682
7970
|
if (pkg.dependencies || pkg.devDependencies) {
|
|
7683
|
-
if (
|
|
7684
|
-
const srcDir =
|
|
7685
|
-
if (
|
|
7971
|
+
if (fs24.existsSync(path24.join(root, "tsconfig.json"))) return { runner: "tsc", baseCommand: "npx tsc --noEmit" };
|
|
7972
|
+
const srcDir = path24.join(root, "src");
|
|
7973
|
+
if (fs24.existsSync(srcDir)) return { runner: "node", baseCommand: `node -c "${srcDir}/**/*.js" 2>/dev/null || node --check $(find src -name '*.js' 2>/dev/null | head -5)` };
|
|
7686
7974
|
}
|
|
7687
7975
|
} catch {
|
|
7688
7976
|
}
|
|
@@ -7782,7 +8070,7 @@ async function runAutoVerification(options = {}) {
|
|
|
7782
8070
|
const heartbeatTimer = setInterval(() => {
|
|
7783
8071
|
if (getLockDir(root)) {
|
|
7784
8072
|
try {
|
|
7785
|
-
|
|
8073
|
+
fs24.writeFileSync(getPidFile(root), String(process.pid), "utf-8");
|
|
7786
8074
|
} catch {
|
|
7787
8075
|
}
|
|
7788
8076
|
}
|
|
@@ -7859,8 +8147,8 @@ __export(init_exports, {
|
|
|
7859
8147
|
generateInitMdContent: () => generateInitMdContent,
|
|
7860
8148
|
runInit: () => runInit
|
|
7861
8149
|
});
|
|
7862
|
-
import
|
|
7863
|
-
import
|
|
8150
|
+
import fs25 from "fs";
|
|
8151
|
+
import path25 from "path";
|
|
7864
8152
|
function configFilePath(type) {
|
|
7865
8153
|
switch (type) {
|
|
7866
8154
|
case "claude":
|
|
@@ -8432,11 +8720,13 @@ function generateInitMdContent() {
|
|
|
8432
8720
|
" 5. kuma_memory \u2192 research_save \u2014 After exploring area (creates search cache)",
|
|
8433
8721
|
" 6. kuma_memory \u2192 gotcha \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)",
|
|
8434
8722
|
" 7. kuma_memory \u2192 arch_flow \u2014 After tracing COMPLETE flow (max 5 core files)",
|
|
8435
|
-
" 8. kuma_memory \u2192
|
|
8436
|
-
" 9.
|
|
8437
|
-
" 10.
|
|
8723
|
+
" 8. kuma_memory \u2192 feature \u2014 \u2B50 When discovering a module/feature boundary (Authentication, Billing)",
|
|
8724
|
+
" 9. kuma_memory \u2192 decision \u2014 When choosing (preserves rationale)",
|
|
8725
|
+
" 10. kuma_safety \u2192 verify \u2014 Run tests / confirm nothing broken",
|
|
8726
|
+
" 11. kuma_context \u2192 changes \u2014 Review session activity",
|
|
8438
8727
|
"",
|
|
8439
8728
|
"\u{1F525} `arch_flow` + `gotcha` are EXPONENTIAL \u2014 each record saves 5-10 files next session.",
|
|
8729
|
+
"\u2B50 `feature` captures module ownership \u2014 use when you identify a cohesive feature boundary.",
|
|
8440
8730
|
"\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
|
|
8441
8731
|
"\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
|
|
8442
8732
|
"",
|
|
@@ -8546,10 +8836,10 @@ function generateInitMdContent() {
|
|
|
8546
8836
|
].join("\n");
|
|
8547
8837
|
}
|
|
8548
8838
|
function handleOpencodeSecondary(root, results) {
|
|
8549
|
-
const skillPath =
|
|
8839
|
+
const skillPath = path25.resolve(root, ".agents", "skills", "kuma", "SKILL.md");
|
|
8550
8840
|
if (results.some((r) => r.filePath === ".agents/skills/kuma/SKILL.md")) return;
|
|
8551
8841
|
try {
|
|
8552
|
-
const dir =
|
|
8842
|
+
const dir = path25.dirname(skillPath);
|
|
8553
8843
|
const content = [
|
|
8554
8844
|
"---",
|
|
8555
8845
|
"name: kuma-mcp",
|
|
@@ -8582,18 +8872,18 @@ function handleOpencodeSecondary(root, results) {
|
|
|
8582
8872
|
"",
|
|
8583
8873
|
"\u{1F4D6} Full rules: `.kuma/init.md`"
|
|
8584
8874
|
].join("\n");
|
|
8585
|
-
if (
|
|
8586
|
-
const existing =
|
|
8875
|
+
if (fs25.existsSync(skillPath)) {
|
|
8876
|
+
const existing = fs25.readFileSync(skillPath, "utf-8");
|
|
8587
8877
|
if (existing.includes("kuma-mcp")) {
|
|
8588
8878
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "skipped" });
|
|
8589
8879
|
return;
|
|
8590
8880
|
}
|
|
8591
8881
|
const newContent = existing.trimEnd() + "\n\n---\n\n" + content;
|
|
8592
|
-
|
|
8882
|
+
fs25.writeFileSync(skillPath, newContent, "utf-8");
|
|
8593
8883
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "appended" });
|
|
8594
8884
|
} else {
|
|
8595
|
-
if (!
|
|
8596
|
-
|
|
8885
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
8886
|
+
fs25.writeFileSync(skillPath, content, "utf-8");
|
|
8597
8887
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "created" });
|
|
8598
8888
|
}
|
|
8599
8889
|
} catch (err) {
|
|
@@ -8606,21 +8896,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
8606
8896
|
}
|
|
8607
8897
|
}
|
|
8608
8898
|
function handleCodexSecondary(root, results) {
|
|
8609
|
-
const tomlPath =
|
|
8899
|
+
const tomlPath = path25.resolve(root, ".codex/config.toml");
|
|
8610
8900
|
if (results.some((r) => r.filePath === ".codex/config.toml")) return;
|
|
8611
8901
|
try {
|
|
8612
|
-
const dir =
|
|
8613
|
-
if (!
|
|
8614
|
-
if (
|
|
8615
|
-
const existingContent =
|
|
8902
|
+
const dir = path25.dirname(tomlPath);
|
|
8903
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
8904
|
+
if (fs25.existsSync(tomlPath)) {
|
|
8905
|
+
const existingContent = fs25.readFileSync(tomlPath, "utf-8");
|
|
8616
8906
|
if (existingContent.includes("kuma")) {
|
|
8617
8907
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "skipped" });
|
|
8618
8908
|
return;
|
|
8619
8909
|
}
|
|
8620
|
-
|
|
8910
|
+
fs25.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
|
|
8621
8911
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "appended" });
|
|
8622
8912
|
} else {
|
|
8623
|
-
|
|
8913
|
+
fs25.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
|
|
8624
8914
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "created" });
|
|
8625
8915
|
}
|
|
8626
8916
|
} catch (err) {
|
|
@@ -8633,11 +8923,11 @@ function handleCodexSecondary(root, results) {
|
|
|
8633
8923
|
}
|
|
8634
8924
|
}
|
|
8635
8925
|
function handleQwenSecondary(root, results) {
|
|
8636
|
-
const settingsPath =
|
|
8926
|
+
const settingsPath = path25.resolve(root, "settings.json");
|
|
8637
8927
|
if (results.some((r) => r.filePath === "settings.json")) return;
|
|
8638
8928
|
try {
|
|
8639
|
-
if (
|
|
8640
|
-
const existingContent =
|
|
8929
|
+
if (fs25.existsSync(settingsPath)) {
|
|
8930
|
+
const existingContent = fs25.readFileSync(settingsPath, "utf-8");
|
|
8641
8931
|
if (existingContent.includes("kuma")) {
|
|
8642
8932
|
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
8643
8933
|
try {
|
|
@@ -8645,7 +8935,7 @@ function handleQwenSecondary(root, results) {
|
|
|
8645
8935
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
8646
8936
|
if (!parsed.mcpServers.kuma) {
|
|
8647
8937
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
8648
|
-
|
|
8938
|
+
fs25.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
8649
8939
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
8650
8940
|
return;
|
|
8651
8941
|
}
|
|
@@ -8660,13 +8950,13 @@ function handleQwenSecondary(root, results) {
|
|
|
8660
8950
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
8661
8951
|
if (!parsed.mcpServers.kuma) {
|
|
8662
8952
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
8663
|
-
|
|
8953
|
+
fs25.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
8664
8954
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
8665
8955
|
}
|
|
8666
8956
|
} catch {
|
|
8667
8957
|
}
|
|
8668
8958
|
} else {
|
|
8669
|
-
|
|
8959
|
+
fs25.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
|
|
8670
8960
|
results.push({ type: "qwen", filePath: "settings.json", action: "created" });
|
|
8671
8961
|
}
|
|
8672
8962
|
} catch (err) {
|
|
@@ -8769,18 +9059,18 @@ function getCombinedAgentsMd(selectedTypes) {
|
|
|
8769
9059
|
return lines.join("\n");
|
|
8770
9060
|
}
|
|
8771
9061
|
function handleAntigravityMcpConfig(root, results) {
|
|
8772
|
-
const mcpPath =
|
|
9062
|
+
const mcpPath = path25.resolve(root, ".agents/mcp_config.json");
|
|
8773
9063
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
8774
9064
|
try {
|
|
8775
|
-
const mcpDir =
|
|
8776
|
-
if (
|
|
8777
|
-
const existingContent =
|
|
9065
|
+
const mcpDir = path25.dirname(mcpPath);
|
|
9066
|
+
if (fs25.existsSync(mcpPath)) {
|
|
9067
|
+
const existingContent = fs25.readFileSync(mcpPath, "utf-8");
|
|
8778
9068
|
if (existingContent.includes("kuma")) {
|
|
8779
9069
|
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
8780
9070
|
const trimmed = existingContent.trimEnd();
|
|
8781
9071
|
if (trimmed.endsWith("}")) {
|
|
8782
9072
|
const updated = trimmed.slice(0, -1).trimEnd() + ',\n "_kuma_note": "Kuma MCP - Generated by kuma init"\n}\n';
|
|
8783
|
-
|
|
9073
|
+
fs25.writeFileSync(mcpPath, updated, "utf-8");
|
|
8784
9074
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
8785
9075
|
}
|
|
8786
9076
|
}
|
|
@@ -8789,11 +9079,11 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
8789
9079
|
const parsed = JSON.parse(existingContent);
|
|
8790
9080
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
8791
9081
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
8792
|
-
|
|
9082
|
+
fs25.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
8793
9083
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
8794
9084
|
} else {
|
|
8795
|
-
if (!
|
|
8796
|
-
|
|
9085
|
+
if (!fs25.existsSync(mcpDir)) fs25.mkdirSync(mcpDir, { recursive: true });
|
|
9086
|
+
fs25.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
8797
9087
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "created" });
|
|
8798
9088
|
}
|
|
8799
9089
|
} catch (err) {
|
|
@@ -8806,18 +9096,18 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
8806
9096
|
}
|
|
8807
9097
|
}
|
|
8808
9098
|
function handleAiderSecondary(root, results) {
|
|
8809
|
-
const ymlPath =
|
|
9099
|
+
const ymlPath = path25.resolve(root, ".aider.conf.yml");
|
|
8810
9100
|
if (results.some((r) => r.filePath === ".aider.conf.yml")) return;
|
|
8811
9101
|
try {
|
|
8812
9102
|
const conventionsRef = "read: CONVENTIONS.md";
|
|
8813
|
-
if (
|
|
8814
|
-
const existingContent =
|
|
9103
|
+
if (fs25.existsSync(ymlPath)) {
|
|
9104
|
+
const existingContent = fs25.readFileSync(ymlPath, "utf-8");
|
|
8815
9105
|
if (existingContent.includes("CONVENTIONS.md") || existingContent.includes("kuma")) {
|
|
8816
9106
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "skipped" });
|
|
8817
9107
|
return;
|
|
8818
9108
|
}
|
|
8819
9109
|
const newContent = existingContent.trimEnd() + "\n\n# Kuma MCP conventions\n" + conventionsRef + "\n";
|
|
8820
|
-
|
|
9110
|
+
fs25.writeFileSync(ymlPath, newContent, "utf-8");
|
|
8821
9111
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "appended" });
|
|
8822
9112
|
} else {
|
|
8823
9113
|
const content = [
|
|
@@ -8827,7 +9117,7 @@ function handleAiderSecondary(root, results) {
|
|
|
8827
9117
|
conventionsRef,
|
|
8828
9118
|
""
|
|
8829
9119
|
].join("\n");
|
|
8830
|
-
|
|
9120
|
+
fs25.writeFileSync(ymlPath, content, "utf-8");
|
|
8831
9121
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "created" });
|
|
8832
9122
|
}
|
|
8833
9123
|
} catch (err) {
|
|
@@ -8840,10 +9130,10 @@ function handleAiderSecondary(root, results) {
|
|
|
8840
9130
|
}
|
|
8841
9131
|
}
|
|
8842
9132
|
function handleCopilotSecondary(root, results) {
|
|
8843
|
-
const skillPath =
|
|
9133
|
+
const skillPath = path25.resolve(root, ".github/skills/kuma/SKILL.md");
|
|
8844
9134
|
if (results.some((r) => r.filePath === ".github/skills/kuma/SKILL.md")) return;
|
|
8845
9135
|
try {
|
|
8846
|
-
const dir =
|
|
9136
|
+
const dir = path25.dirname(skillPath);
|
|
8847
9137
|
const content = [
|
|
8848
9138
|
"---",
|
|
8849
9139
|
"name: kuma-mcp",
|
|
@@ -8854,17 +9144,17 @@ function handleCopilotSecondary(root, results) {
|
|
|
8854
9144
|
"",
|
|
8855
9145
|
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
8856
9146
|
].join("\n");
|
|
8857
|
-
if (
|
|
8858
|
-
const existingContent =
|
|
9147
|
+
if (fs25.existsSync(skillPath)) {
|
|
9148
|
+
const existingContent = fs25.readFileSync(skillPath, "utf-8");
|
|
8859
9149
|
if (existingContent.includes("kuma")) {
|
|
8860
9150
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "skipped" });
|
|
8861
9151
|
return;
|
|
8862
9152
|
}
|
|
8863
|
-
|
|
9153
|
+
fs25.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
|
|
8864
9154
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "appended" });
|
|
8865
9155
|
} else {
|
|
8866
|
-
if (!
|
|
8867
|
-
|
|
9156
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9157
|
+
fs25.writeFileSync(skillPath, content, "utf-8");
|
|
8868
9158
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "created" });
|
|
8869
9159
|
}
|
|
8870
9160
|
} catch (err) {
|
|
@@ -8877,21 +9167,21 @@ function handleCopilotSecondary(root, results) {
|
|
|
8877
9167
|
}
|
|
8878
9168
|
}
|
|
8879
9169
|
function handleOpenclawSecondary(root, results) {
|
|
8880
|
-
const mcpPath =
|
|
9170
|
+
const mcpPath = path25.resolve(root, ".agents/mcp_config.json");
|
|
8881
9171
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
8882
9172
|
try {
|
|
8883
|
-
const dir =
|
|
8884
|
-
if (
|
|
8885
|
-
const existingContent =
|
|
9173
|
+
const dir = path25.dirname(mcpPath);
|
|
9174
|
+
if (fs25.existsSync(mcpPath)) {
|
|
9175
|
+
const existingContent = fs25.readFileSync(mcpPath, "utf-8");
|
|
8886
9176
|
if (existingContent.includes("kuma")) return;
|
|
8887
9177
|
const parsed = JSON.parse(existingContent);
|
|
8888
9178
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
8889
9179
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
8890
|
-
|
|
9180
|
+
fs25.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
8891
9181
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
8892
9182
|
} else {
|
|
8893
|
-
if (!
|
|
8894
|
-
|
|
9183
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9184
|
+
fs25.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
8895
9185
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "created" });
|
|
8896
9186
|
}
|
|
8897
9187
|
} catch (err) {
|
|
@@ -8904,7 +9194,7 @@ function handleOpenclawSecondary(root, results) {
|
|
|
8904
9194
|
}
|
|
8905
9195
|
}
|
|
8906
9196
|
function handleQuickrefGeneration(root, results) {
|
|
8907
|
-
const quickrefPath =
|
|
9197
|
+
const quickrefPath = path25.resolve(root, ".kuma/quickref.md");
|
|
8908
9198
|
const content = [
|
|
8909
9199
|
"# Kuma Quick Reference",
|
|
8910
9200
|
"",
|
|
@@ -8929,16 +9219,16 @@ function handleQuickrefGeneration(root, results) {
|
|
|
8929
9219
|
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
|
|
8930
9220
|
].join("\n");
|
|
8931
9221
|
try {
|
|
8932
|
-
const kumaDir =
|
|
8933
|
-
if (!
|
|
8934
|
-
if (
|
|
8935
|
-
const existing =
|
|
9222
|
+
const kumaDir = path25.dirname(quickrefPath);
|
|
9223
|
+
if (!fs25.existsSync(kumaDir)) fs25.mkdirSync(kumaDir, { recursive: true });
|
|
9224
|
+
if (fs25.existsSync(quickrefPath)) {
|
|
9225
|
+
const existing = fs25.readFileSync(quickrefPath, "utf-8");
|
|
8936
9226
|
if (existing.includes("_Generated by Kuma MCP_")) {
|
|
8937
9227
|
results.push({ type: "claude", filePath: ".kuma/quickref.md", action: "skipped" });
|
|
8938
9228
|
return;
|
|
8939
9229
|
}
|
|
8940
9230
|
}
|
|
8941
|
-
|
|
9231
|
+
fs25.writeFileSync(quickrefPath, content, "utf-8");
|
|
8942
9232
|
results.push({ type: "claude", filePath: ".kuma/quickref.md", action: "created" });
|
|
8943
9233
|
} catch (err) {
|
|
8944
9234
|
results.push({
|
|
@@ -8950,7 +9240,7 @@ function handleQuickrefGeneration(root, results) {
|
|
|
8950
9240
|
}
|
|
8951
9241
|
}
|
|
8952
9242
|
function handleModeMdGeneration(root, results) {
|
|
8953
|
-
const filePath =
|
|
9243
|
+
const filePath = path25.resolve(root, ".kuma/MODE.md");
|
|
8954
9244
|
const content = [
|
|
8955
9245
|
"# Adaptive Mode Selector",
|
|
8956
9246
|
"",
|
|
@@ -8993,10 +9283,10 @@ function handleModeMdGeneration(root, results) {
|
|
|
8993
9283
|
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
|
|
8994
9284
|
].join("\n");
|
|
8995
9285
|
try {
|
|
8996
|
-
const dir =
|
|
8997
|
-
if (!
|
|
8998
|
-
if (!
|
|
8999
|
-
|
|
9286
|
+
const dir = path25.dirname(filePath);
|
|
9287
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9288
|
+
if (!fs25.existsSync(filePath)) {
|
|
9289
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
9000
9290
|
results.push({ type: "claude", filePath: ".kuma/MODE.md", action: "created" });
|
|
9001
9291
|
}
|
|
9002
9292
|
} catch (err) {
|
|
@@ -9004,7 +9294,7 @@ function handleModeMdGeneration(root, results) {
|
|
|
9004
9294
|
}
|
|
9005
9295
|
}
|
|
9006
9296
|
function handleSkipRulesMdGeneration(root, results) {
|
|
9007
|
-
const filePath =
|
|
9297
|
+
const filePath = path25.resolve(root, ".kuma/SKIP_RULES.md");
|
|
9008
9298
|
const content = [
|
|
9009
9299
|
"# Skip Rules \u2014 What NOT to Record",
|
|
9010
9300
|
"",
|
|
@@ -9041,10 +9331,10 @@ function handleSkipRulesMdGeneration(root, results) {
|
|
|
9041
9331
|
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
|
|
9042
9332
|
].join("\n");
|
|
9043
9333
|
try {
|
|
9044
|
-
const dir =
|
|
9045
|
-
if (!
|
|
9046
|
-
if (!
|
|
9047
|
-
|
|
9334
|
+
const dir = path25.dirname(filePath);
|
|
9335
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9336
|
+
if (!fs25.existsSync(filePath)) {
|
|
9337
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
9048
9338
|
results.push({ type: "claude", filePath: ".kuma/SKIP_RULES.md", action: "created" });
|
|
9049
9339
|
}
|
|
9050
9340
|
} catch (err) {
|
|
@@ -9052,7 +9342,7 @@ function handleSkipRulesMdGeneration(root, results) {
|
|
|
9052
9342
|
}
|
|
9053
9343
|
}
|
|
9054
9344
|
function handleStalenessMdGeneration(root, results) {
|
|
9055
|
-
const filePath =
|
|
9345
|
+
const filePath = path25.resolve(root, ".kuma/STALENESS.md");
|
|
9056
9346
|
const content = [
|
|
9057
9347
|
"# Auto-Staleness Detection",
|
|
9058
9348
|
"",
|
|
@@ -9083,10 +9373,10 @@ function handleStalenessMdGeneration(root, results) {
|
|
|
9083
9373
|
"_Generated by Kuma MCP - https://github.com/plumpslabs/kuma_"
|
|
9084
9374
|
].join("\n");
|
|
9085
9375
|
try {
|
|
9086
|
-
const dir =
|
|
9087
|
-
if (!
|
|
9088
|
-
if (!
|
|
9089
|
-
|
|
9376
|
+
const dir = path25.dirname(filePath);
|
|
9377
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9378
|
+
if (!fs25.existsSync(filePath)) {
|
|
9379
|
+
fs25.writeFileSync(filePath, content, "utf-8");
|
|
9090
9380
|
results.push({ type: "claude", filePath: ".kuma/STALENESS.md", action: "created" });
|
|
9091
9381
|
}
|
|
9092
9382
|
} catch (err) {
|
|
@@ -9094,20 +9384,20 @@ function handleStalenessMdGeneration(root, results) {
|
|
|
9094
9384
|
}
|
|
9095
9385
|
}
|
|
9096
9386
|
function handleInitMdGeneration(root, results) {
|
|
9097
|
-
const initMdPath =
|
|
9387
|
+
const initMdPath = path25.resolve(root, ".kuma/init.md");
|
|
9098
9388
|
try {
|
|
9099
|
-
const kumaDir =
|
|
9100
|
-
if (!
|
|
9101
|
-
if (
|
|
9102
|
-
const existing =
|
|
9389
|
+
const kumaDir = path25.dirname(initMdPath);
|
|
9390
|
+
if (!fs25.existsSync(kumaDir)) fs25.mkdirSync(kumaDir, { recursive: true });
|
|
9391
|
+
if (fs25.existsSync(initMdPath)) {
|
|
9392
|
+
const existing = fs25.readFileSync(initMdPath, "utf-8");
|
|
9103
9393
|
if (existing.includes("_Generated by Kuma MCP_")) {
|
|
9104
9394
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "skipped" });
|
|
9105
9395
|
return;
|
|
9106
9396
|
}
|
|
9107
|
-
|
|
9397
|
+
fs25.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
|
|
9108
9398
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "appended" });
|
|
9109
9399
|
} else {
|
|
9110
|
-
|
|
9400
|
+
fs25.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
|
|
9111
9401
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "created" });
|
|
9112
9402
|
}
|
|
9113
9403
|
} catch (err) {
|
|
@@ -9124,20 +9414,20 @@ function handleInitMdGeneration(root, results) {
|
|
|
9124
9414
|
handleStalenessMdGeneration(root, results);
|
|
9125
9415
|
}
|
|
9126
9416
|
function handleCodewhaleSecondary(root, results) {
|
|
9127
|
-
const mcpPath =
|
|
9417
|
+
const mcpPath = path25.resolve(root, ".codewhale/mcp.json");
|
|
9128
9418
|
if (results.some((r) => r.filePath === ".codewhale/mcp.json")) return;
|
|
9129
9419
|
try {
|
|
9130
|
-
const dir =
|
|
9131
|
-
if (
|
|
9132
|
-
const existingContent =
|
|
9420
|
+
const dir = path25.dirname(mcpPath);
|
|
9421
|
+
if (fs25.existsSync(mcpPath)) {
|
|
9422
|
+
const existingContent = fs25.readFileSync(mcpPath, "utf-8");
|
|
9133
9423
|
if (existingContent.includes("kuma")) return;
|
|
9134
9424
|
const parsed = JSON.parse(existingContent);
|
|
9135
9425
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
9136
9426
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
9137
|
-
|
|
9427
|
+
fs25.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
9138
9428
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "appended" });
|
|
9139
9429
|
} else {
|
|
9140
|
-
if (!
|
|
9430
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9141
9431
|
const config = {
|
|
9142
9432
|
mcpServers: {
|
|
9143
9433
|
kuma: {
|
|
@@ -9147,7 +9437,7 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
9147
9437
|
}
|
|
9148
9438
|
}
|
|
9149
9439
|
};
|
|
9150
|
-
|
|
9440
|
+
fs25.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
9151
9441
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "created" });
|
|
9152
9442
|
}
|
|
9153
9443
|
} catch (err) {
|
|
@@ -9169,28 +9459,28 @@ function runInit(options) {
|
|
|
9169
9459
|
let agentsMdHandled = false;
|
|
9170
9460
|
for (const type of selected) {
|
|
9171
9461
|
const relativePath = configFilePath(type);
|
|
9172
|
-
const fullPath =
|
|
9462
|
+
const fullPath = path25.resolve(root, relativePath);
|
|
9173
9463
|
const getTemplate = TEMPLATES[type];
|
|
9174
9464
|
try {
|
|
9175
9465
|
if (AGENTS_MD_TYPES.includes(type) && !agentsMdHandled) {
|
|
9176
9466
|
agentsMdHandled = true;
|
|
9177
9467
|
const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
|
|
9178
|
-
if (
|
|
9468
|
+
if (fs25.existsSync(fullPath)) {
|
|
9179
9469
|
if (options.skipExisting) {
|
|
9180
9470
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
9181
9471
|
} else {
|
|
9182
|
-
const existingContent =
|
|
9472
|
+
const existingContent = fs25.readFileSync(fullPath, "utf-8");
|
|
9183
9473
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
9184
9474
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
9185
9475
|
} else {
|
|
9186
|
-
|
|
9476
|
+
fs25.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
|
|
9187
9477
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
9188
9478
|
}
|
|
9189
9479
|
}
|
|
9190
9480
|
} else {
|
|
9191
|
-
const dir =
|
|
9192
|
-
if (!
|
|
9193
|
-
|
|
9481
|
+
const dir = path25.dirname(fullPath);
|
|
9482
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
9483
|
+
fs25.writeFileSync(fullPath, combinedContent, "utf-8");
|
|
9194
9484
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
9195
9485
|
}
|
|
9196
9486
|
if (selectedSet.has("opencode")) handleOpencodeSecondary(root, results);
|
|
@@ -9202,12 +9492,12 @@ function runInit(options) {
|
|
|
9202
9492
|
continue;
|
|
9203
9493
|
} else {
|
|
9204
9494
|
const template = getTemplate();
|
|
9205
|
-
if (
|
|
9495
|
+
if (fs25.existsSync(fullPath)) {
|
|
9206
9496
|
if (options.skipExisting) {
|
|
9207
9497
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
9208
9498
|
continue;
|
|
9209
9499
|
}
|
|
9210
|
-
const existingContent =
|
|
9500
|
+
const existingContent = fs25.readFileSync(fullPath, "utf-8");
|
|
9211
9501
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
9212
9502
|
if (type === "antigravity") {
|
|
9213
9503
|
handleAntigravityMcpConfig(root, results);
|
|
@@ -9220,14 +9510,14 @@ function runInit(options) {
|
|
|
9220
9510
|
continue;
|
|
9221
9511
|
}
|
|
9222
9512
|
const newContent = existingContent.trimEnd() + APPEND_SEPARATOR + template;
|
|
9223
|
-
|
|
9513
|
+
fs25.writeFileSync(fullPath, newContent, "utf-8");
|
|
9224
9514
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
9225
9515
|
} else {
|
|
9226
|
-
const dir =
|
|
9227
|
-
if (!
|
|
9228
|
-
|
|
9516
|
+
const dir = path25.dirname(fullPath);
|
|
9517
|
+
if (!fs25.existsSync(dir)) {
|
|
9518
|
+
fs25.mkdirSync(dir, { recursive: true });
|
|
9229
9519
|
}
|
|
9230
|
-
|
|
9520
|
+
fs25.writeFileSync(fullPath, template, "utf-8");
|
|
9231
9521
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
9232
9522
|
}
|
|
9233
9523
|
if (type === "antigravity") {
|
|
@@ -9383,19 +9673,19 @@ __export(agentDetector_exports, {
|
|
|
9383
9673
|
getSkillPath: () => getSkillPath,
|
|
9384
9674
|
skillExists: () => skillExists
|
|
9385
9675
|
});
|
|
9386
|
-
import
|
|
9387
|
-
import
|
|
9676
|
+
import fs26 from "fs";
|
|
9677
|
+
import path26 from "path";
|
|
9388
9678
|
function checkFile(root, filePath) {
|
|
9389
9679
|
try {
|
|
9390
|
-
return
|
|
9680
|
+
return fs26.existsSync(path26.join(root, filePath));
|
|
9391
9681
|
} catch {
|
|
9392
9682
|
return false;
|
|
9393
9683
|
}
|
|
9394
9684
|
}
|
|
9395
9685
|
function checkDir(root, dirPath) {
|
|
9396
9686
|
try {
|
|
9397
|
-
const fullPath =
|
|
9398
|
-
return
|
|
9687
|
+
const fullPath = path26.join(root, dirPath);
|
|
9688
|
+
return fs26.existsSync(fullPath) && fs26.statSync(fullPath).isDirectory();
|
|
9399
9689
|
} catch {
|
|
9400
9690
|
return false;
|
|
9401
9691
|
}
|
|
@@ -9463,7 +9753,7 @@ function getAgentLabel(type) {
|
|
|
9463
9753
|
function skillExists(type, projectRoot) {
|
|
9464
9754
|
const root = projectRoot ?? getProjectRoot();
|
|
9465
9755
|
const skillPath = getSkillPath(type);
|
|
9466
|
-
return
|
|
9756
|
+
return fs26.existsSync(path26.join(root, skillPath));
|
|
9467
9757
|
}
|
|
9468
9758
|
var AGENT_DETECTORS;
|
|
9469
9759
|
var init_agentDetector = __esm({
|
|
@@ -9863,11 +10153,11 @@ var init_skillGenerator = __esm({
|
|
|
9863
10153
|
"Project knowledge persists in `.kuma/memories/*.md` across sessions.",
|
|
9864
10154
|
"",
|
|
9865
10155
|
"\u26A1 **ADAPTIVE WORKFLOW (Lean Mode by Default):**",
|
|
9866
|
-
" \u2022 **Lean Mode (Default, < 3 files):** `init` \u2192 edit/read \u2192 record (`decision`/`gotcha` if needed)",
|
|
10156
|
+
" \u2022 **Lean Mode (Default, < 3 files):** `init` \u2192 edit/read \u2192 record (`decision`/`gotcha`/`feature` if needed)",
|
|
9867
10157
|
" \u2022 **Standard Mode (3-10 files/unfamiliar):** `init` \u2192 `guard` \u2192 `research` \u2192 edit \u2192 `record` \u2192 `verify`",
|
|
9868
10158
|
" \u2022 **Full Mode (> 10 files/refactor):** `init` \u2192 `guard` \u2192 `research` \u2192 `impact` \u2192 edit \u2192 `record` \u2192 `verify` \u2192 `changes`",
|
|
9869
10159
|
"",
|
|
9870
|
-
"\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow)",
|
|
10160
|
+
"\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow), `feature` (high-level module ownership)",
|
|
9871
10161
|
"\u{1F7E2} **SKIP RECORDING:** function, class, import, route (use native grep/glob instead)",
|
|
9872
10162
|
"",
|
|
9873
10163
|
"\u{1F4D6} Full rules & modes: `.kuma/init.md` | `.kuma/MODE.md` | `.kuma/SKIP_RULES.md`",
|
|
@@ -9883,7 +10173,7 @@ var init_skillGenerator = __esm({
|
|
|
9883
10173
|
" \u2022 **Standard Mode (3-10 files/unfamiliar):** `init` \u2192 `guard` \u2192 `research` \u2192 edit \u2192 `record` \u2192 `verify`",
|
|
9884
10174
|
" \u2022 **Full Mode (> 10 files/refactor):** `init` \u2192 `guard` \u2192 `research` \u2192 `impact` \u2192 edit \u2192 `record` \u2192 `verify` \u2192 `changes`",
|
|
9885
10175
|
"",
|
|
9886
|
-
"\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow)",
|
|
10176
|
+
"\u{1F534} **MUST RECORD:** `decision` (rationale), `gotcha` (bug/quirk), `arch_flow` (complex flow), `feature` (high-level module ownership)",
|
|
9887
10177
|
"\u{1F7E2} **SKIP RECORDING:** function, class, import, route (use native grep/glob instead)",
|
|
9888
10178
|
"",
|
|
9889
10179
|
"\u{1F4D6} Full rules & modes: `.kuma/init.md` | `.kuma/MODE.md` | `.kuma/SKIP_RULES.md`",
|
|
@@ -10407,9 +10697,58 @@ init_sessionMemory();
|
|
|
10407
10697
|
init_kumaDb();
|
|
10408
10698
|
init_kumaSelfHeal();
|
|
10409
10699
|
init_kumaMemory();
|
|
10700
|
+
|
|
10701
|
+
// src/engine/kumaNoiseFilter.ts
|
|
10702
|
+
var ALLOWED_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
10703
|
+
"arch_flow",
|
|
10704
|
+
// Architecture flow (Layer 2)
|
|
10705
|
+
"gotcha",
|
|
10706
|
+
// Known gotchas / edge cases (Layer 3)
|
|
10707
|
+
"decision",
|
|
10708
|
+
// Architecture decisions / ADRs
|
|
10709
|
+
"cross_service_link",
|
|
10710
|
+
// Backend ↔ Frontend integration links
|
|
10711
|
+
"feature_domain",
|
|
10712
|
+
// High-level feature domains
|
|
10713
|
+
"file",
|
|
10714
|
+
// File references (needed for graph connectivity)
|
|
10715
|
+
"research"
|
|
10716
|
+
// Research cache nodes (needed for search)
|
|
10717
|
+
]);
|
|
10718
|
+
function isNodeTypeAllowed(type) {
|
|
10719
|
+
return ALLOWED_NODE_TYPES.has(type);
|
|
10720
|
+
}
|
|
10721
|
+
function getNoiseFilterPolicy() {
|
|
10722
|
+
return [
|
|
10723
|
+
"\u{1F9F9} **Kuma Noise Filter \u2014 Anti-AST Guard**",
|
|
10724
|
+
"\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",
|
|
10725
|
+
"",
|
|
10726
|
+
"**Allowed High-Value Node Types:**",
|
|
10727
|
+
" \u{1F3DB}\uFE0F `arch_flow` \u2014 Architecture flow (max 5 core files per flow)",
|
|
10728
|
+
" \u{1F4CC} `gotcha` \u2014 Known bugs, edge cases, gotchas",
|
|
10729
|
+
" \u2696\uFE0F `decision` \u2014 Architecture decisions / ADRs",
|
|
10730
|
+
" \u{1F517} `cross_service_link` \u2014 Backend \u2194 Frontend integration links",
|
|
10731
|
+
" \u{1F3D7}\uFE0F `feature_domain` \u2014 High-level feature domains",
|
|
10732
|
+
" \u{1F4C4} `file` \u2014 File references (graph connectivity)",
|
|
10733
|
+
" \u{1F52C} `research` \u2014 Research cache nodes",
|
|
10734
|
+
"",
|
|
10735
|
+
"**Rejected Noise Types:**",
|
|
10736
|
+
" \u274C `function`, `class`, `component`, `variable`, `method`",
|
|
10737
|
+
" \u274C `interface`, `type`, `enum`, `const`, `hook`",
|
|
10738
|
+
" \u274C `route`, `api_route`, `test`",
|
|
10739
|
+
"",
|
|
10740
|
+
"**Why?** AST-level nodes create graph junk that slows retrieval.",
|
|
10741
|
+
"Use `arch_flow` to describe HOW files connect, not individual functions.",
|
|
10742
|
+
"",
|
|
10743
|
+
"**Tip:** Instead of adding function nodes, record an `arch_flow`:",
|
|
10744
|
+
" kuma_memory({ action: 'arch_flow', content: 'domain: AuthFlow | hops: auth.ts \u2192 middleware.ts \u2192 session.ts' })"
|
|
10745
|
+
].join("\n");
|
|
10746
|
+
}
|
|
10747
|
+
|
|
10748
|
+
// src/tools/kumaMemoryTool.ts
|
|
10410
10749
|
init_pathValidator();
|
|
10411
|
-
import
|
|
10412
|
-
import
|
|
10750
|
+
import fs15 from "fs";
|
|
10751
|
+
import path15 from "path";
|
|
10413
10752
|
var MEMORY_ALIASES = {
|
|
10414
10753
|
// Session synonyms
|
|
10415
10754
|
"session": "session",
|
|
@@ -10515,7 +10854,18 @@ var MEMORY_ALIASES = {
|
|
|
10515
10854
|
"clear-graph": "clear",
|
|
10516
10855
|
"wipe": "clear",
|
|
10517
10856
|
"reset": "clear",
|
|
10518
|
-
"purge-all": "clear"
|
|
10857
|
+
"purge-all": "clear",
|
|
10858
|
+
// Harvest synonyms (Pilar 5)
|
|
10859
|
+
"harvest": "harvest",
|
|
10860
|
+
"git-harvest": "harvest",
|
|
10861
|
+
"auto-harvest": "harvest",
|
|
10862
|
+
"git": "harvest",
|
|
10863
|
+
// Noise policy synonyms (Pilar 4)
|
|
10864
|
+
"noise_policy": "noise_policy",
|
|
10865
|
+
"noise-policy": "noise_policy",
|
|
10866
|
+
"noise": "noise_policy",
|
|
10867
|
+
"anti-ast": "noise_policy",
|
|
10868
|
+
"filter": "noise_policy"
|
|
10519
10869
|
};
|
|
10520
10870
|
async function handleMemory(params) {
|
|
10521
10871
|
const rawAction = params.action || "session";
|
|
@@ -10566,6 +10916,10 @@ async function handleMemory(params) {
|
|
|
10566
10916
|
await clearGraph2();
|
|
10567
10917
|
return "\u{1F5D1}\uFE0F **Knowledge Graph Cleared** \u2014 All nodes, edges, and gotchas have been wiped from disk and memory.";
|
|
10568
10918
|
}
|
|
10919
|
+
case "harvest":
|
|
10920
|
+
return handleHarvest(params);
|
|
10921
|
+
case "noise_policy":
|
|
10922
|
+
return getNoiseFilterPolicy();
|
|
10569
10923
|
default:
|
|
10570
10924
|
return `Unknown action "${action}".`;
|
|
10571
10925
|
}
|
|
@@ -10640,9 +10994,9 @@ async function handleResearchSave(params) {
|
|
|
10640
10994
|
} catch {
|
|
10641
10995
|
}
|
|
10642
10996
|
try {
|
|
10643
|
-
const researchDir =
|
|
10644
|
-
if (!
|
|
10645
|
-
|
|
10997
|
+
const researchDir = path15.join(getProjectRoot(), ".kuma", "research");
|
|
10998
|
+
if (!fs15.existsSync(researchDir)) fs15.mkdirSync(researchDir, { recursive: true });
|
|
10999
|
+
fs15.writeFileSync(path15.join(researchDir, `${scope}.json`), JSON.stringify(JSON.parse(record), null, 2), "utf-8");
|
|
10646
11000
|
} catch {
|
|
10647
11001
|
}
|
|
10648
11002
|
sessionMemory.recordMemoryAction("research_save");
|
|
@@ -10731,8 +11085,8 @@ ${impactLines.join("\n")}`;
|
|
|
10731
11085
|
}
|
|
10732
11086
|
let hybridResults = "";
|
|
10733
11087
|
try {
|
|
10734
|
-
const {
|
|
10735
|
-
const semanticResults = await
|
|
11088
|
+
const { enhancedHybridSearch: enhancedHybridSearch2, formatHybridResults: formatHybridResults2 } = await Promise.resolve().then(() => (init_kumaSearch(), kumaSearch_exports));
|
|
11089
|
+
const semanticResults = await enhancedHybridSearch2(query, 8);
|
|
10736
11090
|
if (semanticResults.length > 0) {
|
|
10737
11091
|
hybridResults = "\n" + formatHybridResults2(query, semanticResults);
|
|
10738
11092
|
}
|
|
@@ -11009,11 +11363,14 @@ async function handleAddNode(params) {
|
|
|
11009
11363
|
const name = params.content;
|
|
11010
11364
|
const filePath = params.scope;
|
|
11011
11365
|
if (!type || !name) {
|
|
11012
|
-
return "\u26A0\uFE0F `title` (node type
|
|
11366
|
+
return "\u26A0\uFE0F `title` (node type) and `content` (node name) required.\n\n\u2705 Allowed types: arch_flow, gotcha, decision, cross_service_link, feature_domain, file, research\n\u274C Rejected: function, class, component, variable, method\n\nExample: kuma_memory({ action: 'arch_flow', content: 'domain: AuthFlow | hops: auth.ts \u2192 middleware.ts' })";
|
|
11013
11367
|
}
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11368
|
+
if (!isNodeTypeAllowed(type)) {
|
|
11369
|
+
return [
|
|
11370
|
+
`\u274C **Noise Filter:** Type "${type}" is not allowed.`,
|
|
11371
|
+
"",
|
|
11372
|
+
getNoiseFilterPolicy()
|
|
11373
|
+
].join("\n");
|
|
11017
11374
|
}
|
|
11018
11375
|
try {
|
|
11019
11376
|
const { upsertNode: upsertNode2, nodeId: nodeId2, addEdge: addEdge2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
|
|
@@ -11126,78 +11483,88 @@ async function handleLayersSummary(_params) {
|
|
|
11126
11483
|
const { getLayersSummary: getLayersSummary2 } = await Promise.resolve().then(() => (init_domainRules(), domainRules_exports));
|
|
11127
11484
|
return getLayersSummary2();
|
|
11128
11485
|
}
|
|
11486
|
+
async function handleHarvest(params) {
|
|
11487
|
+
sessionMemory.recordToolCall("kuma_memory_harvest", { scope: params.scope });
|
|
11488
|
+
const { harvestGitHistory: harvestGitHistory2, installGitHook: installGitHook2 } = await Promise.resolve().then(() => (init_kumaGitHarvester(), kumaGitHarvester_exports));
|
|
11489
|
+
if (params.scope === "install") {
|
|
11490
|
+
return installGitHook2();
|
|
11491
|
+
}
|
|
11492
|
+
const count = params.limit || 10;
|
|
11493
|
+
const dryRun = params.status === "dry-run" || params.status === "preview";
|
|
11494
|
+
return await harvestGitHistory2({ commitCount: count, dryRun });
|
|
11495
|
+
}
|
|
11129
11496
|
|
|
11130
11497
|
// src/tools/kumaSafetyTool.ts
|
|
11131
11498
|
init_sessionMemory();
|
|
11132
11499
|
|
|
11133
11500
|
// src/engine/kumaLock.ts
|
|
11134
11501
|
init_pathValidator();
|
|
11135
|
-
import
|
|
11136
|
-
import
|
|
11502
|
+
import fs16 from "fs";
|
|
11503
|
+
import path16 from "path";
|
|
11137
11504
|
var LOCKS_DIR = ".kuma/locks";
|
|
11138
11505
|
function locksDir() {
|
|
11139
|
-
return
|
|
11506
|
+
return path16.join(getProjectRoot(), LOCKS_DIR);
|
|
11140
11507
|
}
|
|
11141
11508
|
function ensureLocksDir() {
|
|
11142
11509
|
const dir = locksDir();
|
|
11143
|
-
if (!
|
|
11510
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
11144
11511
|
}
|
|
11145
11512
|
function lockPath(filePath) {
|
|
11146
11513
|
const safeName = filePath.replace(/[^a-zA-Z0-9_./-]/g, "_");
|
|
11147
|
-
return
|
|
11514
|
+
return path16.join(locksDir(), `${safeName}.lock.json`);
|
|
11148
11515
|
}
|
|
11149
11516
|
function acquireLock(filePath, agentId) {
|
|
11150
11517
|
ensureLocksDir();
|
|
11151
11518
|
const lp = lockPath(filePath);
|
|
11152
11519
|
const id = agentId || `agent-${process.pid}`;
|
|
11153
|
-
if (
|
|
11520
|
+
if (fs16.existsSync(lp)) {
|
|
11154
11521
|
try {
|
|
11155
|
-
const existing = JSON.parse(
|
|
11522
|
+
const existing = JSON.parse(fs16.readFileSync(lp, "utf-8"));
|
|
11156
11523
|
if (existing.agentId === id) {
|
|
11157
11524
|
return `\u{1F512} **Already locked** by you (${id}) on ${new Date(existing.acquiredAt).toISOString()}`;
|
|
11158
11525
|
}
|
|
11159
11526
|
const elapsed = Math.floor((Date.now() - existing.acquiredAt) / 1e3);
|
|
11160
11527
|
if (elapsed > 300) {
|
|
11161
|
-
|
|
11528
|
+
fs16.unlinkSync(lp);
|
|
11162
11529
|
} else {
|
|
11163
11530
|
return `\u{1F512} **Locked** by ${existing.agentId} since ${new Date(existing.acquiredAt).toISOString()} (${elapsed}s ago)`;
|
|
11164
11531
|
}
|
|
11165
11532
|
} catch {
|
|
11166
|
-
|
|
11533
|
+
fs16.unlinkSync(lp);
|
|
11167
11534
|
}
|
|
11168
11535
|
}
|
|
11169
11536
|
const entry = { filePath, agentId: id, acquiredAt: Date.now(), status: "locked" };
|
|
11170
|
-
|
|
11537
|
+
fs16.writeFileSync(lp, JSON.stringify(entry, null, 2), "utf-8");
|
|
11171
11538
|
return `\u{1F513} **Lock acquired** on \`${filePath}\` by ${id}`;
|
|
11172
11539
|
}
|
|
11173
11540
|
function releaseLock(filePath, agentId) {
|
|
11174
11541
|
ensureLocksDir();
|
|
11175
11542
|
const lp = lockPath(filePath);
|
|
11176
11543
|
const id = agentId || `agent-${process.pid}`;
|
|
11177
|
-
if (!
|
|
11544
|
+
if (!fs16.existsSync(lp)) {
|
|
11178
11545
|
return `\u26A0\uFE0F No lock found for \`${filePath}\``;
|
|
11179
11546
|
}
|
|
11180
11547
|
try {
|
|
11181
|
-
const existing = JSON.parse(
|
|
11548
|
+
const existing = JSON.parse(fs16.readFileSync(lp, "utf-8"));
|
|
11182
11549
|
if (existing.agentId !== id) {
|
|
11183
11550
|
return `\u26A0\uFE0F Cannot release lock held by ${existing.agentId}. Use force:true to override.`;
|
|
11184
11551
|
}
|
|
11185
|
-
|
|
11552
|
+
fs16.unlinkSync(lp);
|
|
11186
11553
|
return `\u{1F513} **Lock released** on \`${filePath}\``;
|
|
11187
11554
|
} catch {
|
|
11188
|
-
|
|
11555
|
+
fs16.unlinkSync(lp);
|
|
11189
11556
|
return `\u{1F513} **Lock released** (force cleanup) on \`${filePath}\``;
|
|
11190
11557
|
}
|
|
11191
11558
|
}
|
|
11192
11559
|
function listLocks() {
|
|
11193
11560
|
ensureLocksDir();
|
|
11194
11561
|
const dir = locksDir();
|
|
11195
|
-
const files =
|
|
11562
|
+
const files = fs16.readdirSync(dir).filter((f) => f.endsWith(".lock.json"));
|
|
11196
11563
|
if (files.length === 0) return "\u{1F513} No active locks.";
|
|
11197
11564
|
const lines = ["\u{1F512} **Active Locks:**", ""];
|
|
11198
11565
|
for (const f of files) {
|
|
11199
11566
|
try {
|
|
11200
|
-
const entry = JSON.parse(
|
|
11567
|
+
const entry = JSON.parse(fs16.readFileSync(path16.join(dir, f), "utf-8"));
|
|
11201
11568
|
const elapsed = Math.floor((Date.now() - entry.acquiredAt) / 1e3);
|
|
11202
11569
|
lines.push(` \u2022 \`${entry.filePath}\` \u2014 locked by ${entry.agentId} (${elapsed}s ago)`);
|
|
11203
11570
|
} catch {
|
|
@@ -11208,9 +11575,9 @@ function listLocks() {
|
|
|
11208
11575
|
}
|
|
11209
11576
|
function isLocked(filePath) {
|
|
11210
11577
|
const lp = lockPath(filePath);
|
|
11211
|
-
if (!
|
|
11578
|
+
if (!fs16.existsSync(lp)) return { locked: false };
|
|
11212
11579
|
try {
|
|
11213
|
-
const entry = JSON.parse(
|
|
11580
|
+
const entry = JSON.parse(fs16.readFileSync(lp, "utf-8"));
|
|
11214
11581
|
return { locked: true, by: entry.agentId, since: entry.acquiredAt };
|
|
11215
11582
|
} catch {
|
|
11216
11583
|
return { locked: false };
|
|
@@ -11220,16 +11587,16 @@ function cleanStaleLocks() {
|
|
|
11220
11587
|
ensureLocksDir();
|
|
11221
11588
|
const dir = locksDir();
|
|
11222
11589
|
let count = 0;
|
|
11223
|
-
for (const f of
|
|
11590
|
+
for (const f of fs16.readdirSync(dir).filter((f2) => f2.endsWith(".lock.json"))) {
|
|
11224
11591
|
try {
|
|
11225
|
-
const entry = JSON.parse(
|
|
11592
|
+
const entry = JSON.parse(fs16.readFileSync(path16.join(dir, f), "utf-8"));
|
|
11226
11593
|
if (Date.now() - entry.acquiredAt > 3e5) {
|
|
11227
|
-
|
|
11594
|
+
fs16.unlinkSync(path16.join(dir, f));
|
|
11228
11595
|
count++;
|
|
11229
11596
|
}
|
|
11230
11597
|
} catch {
|
|
11231
11598
|
try {
|
|
11232
|
-
|
|
11599
|
+
fs16.unlinkSync(path16.join(dir, f));
|
|
11233
11600
|
count++;
|
|
11234
11601
|
} catch {
|
|
11235
11602
|
}
|
|
@@ -11245,8 +11612,8 @@ init_safetyAudit();
|
|
|
11245
11612
|
|
|
11246
11613
|
// src/tools/kumaPolicy.ts
|
|
11247
11614
|
init_pathValidator();
|
|
11248
|
-
import
|
|
11249
|
-
import
|
|
11615
|
+
import fs17 from "fs";
|
|
11616
|
+
import path17 from "path";
|
|
11250
11617
|
var DEFAULT_POLICY = {
|
|
11251
11618
|
never_touch: [
|
|
11252
11619
|
".env",
|
|
@@ -11272,15 +11639,15 @@ var DEFAULT_POLICY = {
|
|
|
11272
11639
|
};
|
|
11273
11640
|
function loadPolicy() {
|
|
11274
11641
|
const root = getProjectRoot();
|
|
11275
|
-
const ymlPath =
|
|
11276
|
-
const yamlPath =
|
|
11642
|
+
const ymlPath = path17.join(root, ".kuma", "policy.yml");
|
|
11643
|
+
const yamlPath = path17.join(root, ".kuma", "policy.yaml");
|
|
11277
11644
|
let policyContent = null;
|
|
11278
11645
|
let policyPath = null;
|
|
11279
|
-
if (
|
|
11280
|
-
policyContent =
|
|
11646
|
+
if (fs17.existsSync(ymlPath)) {
|
|
11647
|
+
policyContent = fs17.readFileSync(ymlPath, "utf-8");
|
|
11281
11648
|
policyPath = ymlPath;
|
|
11282
|
-
} else if (
|
|
11283
|
-
policyContent =
|
|
11649
|
+
} else if (fs17.existsSync(yamlPath)) {
|
|
11650
|
+
policyContent = fs17.readFileSync(yamlPath, "utf-8");
|
|
11284
11651
|
policyPath = yamlPath;
|
|
11285
11652
|
}
|
|
11286
11653
|
if (!policyContent) {
|
|
@@ -11390,9 +11757,9 @@ function checkFilePathPolicy(filePath, policy) {
|
|
|
11390
11757
|
if (policy.max_file_size && policy.max_file_size > 0) {
|
|
11391
11758
|
try {
|
|
11392
11759
|
const root = getProjectRoot();
|
|
11393
|
-
const fullPath =
|
|
11394
|
-
if (
|
|
11395
|
-
const sizeKB =
|
|
11760
|
+
const fullPath = path17.join(root, filePath);
|
|
11761
|
+
if (fs17.existsSync(fullPath)) {
|
|
11762
|
+
const sizeKB = fs17.statSync(fullPath).size / 1024;
|
|
11396
11763
|
if (sizeKB > policy.max_file_size) {
|
|
11397
11764
|
violations.push({
|
|
11398
11765
|
rule: "max_file_size",
|
|
@@ -11559,12 +11926,12 @@ async function safetyCheck(action, filePath, command) {
|
|
|
11559
11926
|
}
|
|
11560
11927
|
if (filePath) {
|
|
11561
11928
|
try {
|
|
11562
|
-
const
|
|
11563
|
-
const
|
|
11929
|
+
const fs27 = await import("fs");
|
|
11930
|
+
const path27 = await import("path");
|
|
11564
11931
|
const root = process.cwd();
|
|
11565
|
-
const fullPath =
|
|
11566
|
-
if (
|
|
11567
|
-
const stat =
|
|
11932
|
+
const fullPath = path27.resolve(root, filePath);
|
|
11933
|
+
if (fs27.existsSync(fullPath)) {
|
|
11934
|
+
const stat = fs27.statSync(fullPath);
|
|
11568
11935
|
const sizeKB = Math.round(stat.size / 1024);
|
|
11569
11936
|
if (sizeKB > 500) {
|
|
11570
11937
|
checks.push({
|
|
@@ -11574,12 +11941,12 @@ async function safetyCheck(action, filePath, command) {
|
|
|
11574
11941
|
});
|
|
11575
11942
|
}
|
|
11576
11943
|
if (filePath.endsWith(".ts") || filePath.endsWith(".js")) {
|
|
11577
|
-
const baseName =
|
|
11944
|
+
const baseName = path27.basename(filePath, path27.extname(filePath));
|
|
11578
11945
|
const testPatterns = [
|
|
11579
11946
|
filePath.replace(/\.(ts|js)$/, ".test.$1"),
|
|
11580
11947
|
filePath.replace(/\.(ts|js)$/, ".spec.$1"),
|
|
11581
11948
|
filePath.replace(/^src\//, "tests/").replace(/\.(ts|js)$/, ".test.$1"),
|
|
11582
|
-
`**/__tests__/**/${baseName}.test.${
|
|
11949
|
+
`**/__tests__/**/${baseName}.test.${path27.extname(filePath).slice(1)}`
|
|
11583
11950
|
];
|
|
11584
11951
|
let hasTests = false;
|
|
11585
11952
|
const fg = await import("fast-glob");
|
|
@@ -11595,7 +11962,7 @@ async function safetyCheck(action, filePath, command) {
|
|
|
11595
11962
|
name: "Tests Check",
|
|
11596
11963
|
passed: true,
|
|
11597
11964
|
// Not a blocker, just informative
|
|
11598
|
-
detail: `\u{1F9EA} No test file found for "${
|
|
11965
|
+
detail: `\u{1F9EA} No test file found for "${path27.basename(filePath)}" \u2014 consider adding tests`
|
|
11599
11966
|
});
|
|
11600
11967
|
} else {
|
|
11601
11968
|
checks.push({
|
|
@@ -11684,9 +12051,9 @@ init_sessionMemory();
|
|
|
11684
12051
|
// src/guards/antiPatternDetector.ts
|
|
11685
12052
|
init_pathValidator();
|
|
11686
12053
|
init_sessionMemory();
|
|
11687
|
-
import
|
|
11688
|
-
import
|
|
11689
|
-
import { execSync as
|
|
12054
|
+
import fs18 from "fs";
|
|
12055
|
+
import path18 from "path";
|
|
12056
|
+
import { execSync as execSync6 } from "child_process";
|
|
11690
12057
|
var SCRIPT_PATCH_PATTERNS = [
|
|
11691
12058
|
"writeFileSync",
|
|
11692
12059
|
"writeFile",
|
|
@@ -11712,20 +12079,20 @@ function findPatchScripts(projectRoot) {
|
|
|
11712
12079
|
const warnings = [];
|
|
11713
12080
|
const recentFiles = scanRecentFiles(projectRoot);
|
|
11714
12081
|
for (const file of recentFiles) {
|
|
11715
|
-
const ext =
|
|
12082
|
+
const ext = path18.extname(file).toLowerCase();
|
|
11716
12083
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
11717
|
-
const relativePath =
|
|
12084
|
+
const relativePath = path18.relative(projectRoot, file);
|
|
11718
12085
|
if (relativePath.startsWith("src") || relativePath.startsWith("test") || relativePath.startsWith("node_modules") || relativePath.startsWith(".") || relativePath.startsWith(".kuma/scratch")) {
|
|
11719
12086
|
continue;
|
|
11720
12087
|
}
|
|
11721
12088
|
try {
|
|
11722
|
-
const content =
|
|
12089
|
+
const content = fs18.readFileSync(file, "utf-8").toLowerCase();
|
|
11723
12090
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
11724
12091
|
if (matchedPattern) {
|
|
11725
12092
|
warnings.push({
|
|
11726
12093
|
severity: "high",
|
|
11727
12094
|
pattern: "script-patching",
|
|
11728
|
-
message: `Created script file that modifies other files: ${
|
|
12095
|
+
message: `Created script file that modifies other files: ${path18.basename(file)}`,
|
|
11729
12096
|
suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
|
|
11730
12097
|
evidence: `File: ${relativePath} contains '${matchedPattern}'`,
|
|
11731
12098
|
filePath: relativePath
|
|
@@ -11759,15 +12126,15 @@ function detectBashGrepUsage() {
|
|
|
11759
12126
|
function scanRecentFiles(projectRoot) {
|
|
11760
12127
|
const recent = [];
|
|
11761
12128
|
try {
|
|
11762
|
-
const entries =
|
|
12129
|
+
const entries = fs18.readdirSync(projectRoot, { withFileTypes: true });
|
|
11763
12130
|
const now = Date.now();
|
|
11764
12131
|
const recentThreshold = 30 * 60 * 1e3;
|
|
11765
12132
|
for (const entry of entries) {
|
|
11766
12133
|
if (!entry.isFile()) continue;
|
|
11767
12134
|
try {
|
|
11768
|
-
const stat =
|
|
12135
|
+
const stat = fs18.statSync(path18.join(projectRoot, entry.name));
|
|
11769
12136
|
if (now - stat.mtimeMs < recentThreshold || now - stat.ctimeMs < recentThreshold) {
|
|
11770
|
-
recent.push(
|
|
12137
|
+
recent.push(path18.join(projectRoot, entry.name));
|
|
11771
12138
|
}
|
|
11772
12139
|
} catch {
|
|
11773
12140
|
continue;
|
|
@@ -11780,7 +12147,7 @@ function scanRecentFiles(projectRoot) {
|
|
|
11780
12147
|
function detectGitPatchScripts(projectRoot) {
|
|
11781
12148
|
const warnings = [];
|
|
11782
12149
|
try {
|
|
11783
|
-
const statusStdout =
|
|
12150
|
+
const statusStdout = execSync6("git status --porcelain", {
|
|
11784
12151
|
cwd: projectRoot,
|
|
11785
12152
|
encoding: "utf-8",
|
|
11786
12153
|
timeout: 3e3
|
|
@@ -11791,14 +12158,14 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
11791
12158
|
const prefix = line.substring(0, 2);
|
|
11792
12159
|
if (prefix !== "??" && prefix !== "A ") continue;
|
|
11793
12160
|
const file = line.substring(3).trim();
|
|
11794
|
-
const ext =
|
|
12161
|
+
const ext = path18.extname(file).toLowerCase();
|
|
11795
12162
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
11796
12163
|
const isRootLevel = !file.includes("/");
|
|
11797
12164
|
const isScriptsDir = file.startsWith("scripts/") || file.startsWith("patches/");
|
|
11798
12165
|
if (!isRootLevel && !isScriptsDir) continue;
|
|
11799
|
-
const fullPath =
|
|
11800
|
-
if (
|
|
11801
|
-
const content =
|
|
12166
|
+
const fullPath = path18.join(projectRoot, file);
|
|
12167
|
+
if (fs18.existsSync(fullPath)) {
|
|
12168
|
+
const content = fs18.readFileSync(fullPath, "utf-8").toLowerCase();
|
|
11802
12169
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
11803
12170
|
if (matchedPattern) {
|
|
11804
12171
|
warnings.push({
|
|
@@ -11813,7 +12180,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
11813
12180
|
}
|
|
11814
12181
|
}
|
|
11815
12182
|
}
|
|
11816
|
-
const deletedStdout =
|
|
12183
|
+
const deletedStdout = execSync6("git diff --name-only --diff-filter=D HEAD", {
|
|
11817
12184
|
cwd: projectRoot,
|
|
11818
12185
|
encoding: "utf-8",
|
|
11819
12186
|
timeout: 3e3
|
|
@@ -11821,7 +12188,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
11821
12188
|
if (deletedStdout) {
|
|
11822
12189
|
const deletedFiles = deletedStdout.split("\n").filter(Boolean);
|
|
11823
12190
|
for (const file of deletedFiles) {
|
|
11824
|
-
const ext =
|
|
12191
|
+
const ext = path18.extname(file).toLowerCase();
|
|
11825
12192
|
if (SCRIPT_EXTENSIONS.includes(ext)) {
|
|
11826
12193
|
warnings.push({
|
|
11827
12194
|
severity: "high",
|
|
@@ -11883,21 +12250,21 @@ function detectAllAntiPatterns() {
|
|
|
11883
12250
|
// src/engine/contextSnapshot.ts
|
|
11884
12251
|
init_sessionMemory();
|
|
11885
12252
|
init_pathValidator();
|
|
11886
|
-
import
|
|
11887
|
-
import
|
|
11888
|
-
import { execSync as
|
|
12253
|
+
import fs19 from "fs";
|
|
12254
|
+
import path19 from "path";
|
|
12255
|
+
import { execSync as execSync7 } from "child_process";
|
|
11889
12256
|
var SNAPSHOT_DIR = "context-snapshots";
|
|
11890
12257
|
function snapshotDir() {
|
|
11891
|
-
return
|
|
12258
|
+
return path19.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
|
|
11892
12259
|
}
|
|
11893
12260
|
function ensureSnapshotDir() {
|
|
11894
12261
|
const dir = snapshotDir();
|
|
11895
|
-
if (!
|
|
11896
|
-
|
|
12262
|
+
if (!fs19.existsSync(dir)) {
|
|
12263
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
11897
12264
|
}
|
|
11898
12265
|
}
|
|
11899
12266
|
function snapshotFilePath(timestamp) {
|
|
11900
|
-
return
|
|
12267
|
+
return path19.join(snapshotDir(), `${timestamp}.json`);
|
|
11901
12268
|
}
|
|
11902
12269
|
function saveSnapshot(goal) {
|
|
11903
12270
|
try {
|
|
@@ -11918,7 +12285,7 @@ function saveSnapshot(goal) {
|
|
|
11918
12285
|
hasConventions: !!summary.hasConventions
|
|
11919
12286
|
};
|
|
11920
12287
|
try {
|
|
11921
|
-
|
|
12288
|
+
fs19.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
11922
12289
|
} catch {
|
|
11923
12290
|
}
|
|
11924
12291
|
sessionMemory.recordToolCall("kuma_context", { action: "save", goal: snapshot.goal });
|
|
@@ -11957,7 +12324,7 @@ function formatSnapshot(snapshot) {
|
|
|
11957
12324
|
function getGitDiffStat2() {
|
|
11958
12325
|
try {
|
|
11959
12326
|
const root = getProjectRoot();
|
|
11960
|
-
const stdout =
|
|
12327
|
+
const stdout = execSync7("git diff --stat", {
|
|
11961
12328
|
cwd: root,
|
|
11962
12329
|
encoding: "utf-8",
|
|
11963
12330
|
timeout: 3e3
|
|
@@ -12229,12 +12596,12 @@ async function handleSecurity(params) {
|
|
|
12229
12596
|
sessionMemory.recordToolCall("kuma_safety_security", {});
|
|
12230
12597
|
if (params.filePath) {
|
|
12231
12598
|
try {
|
|
12232
|
-
const
|
|
12233
|
-
const
|
|
12599
|
+
const fs27 = await import("fs");
|
|
12600
|
+
const path27 = await import("path");
|
|
12234
12601
|
const root = process.cwd();
|
|
12235
|
-
const fullPath =
|
|
12236
|
-
if (
|
|
12237
|
-
const content =
|
|
12602
|
+
const fullPath = path27.resolve(root, params.filePath);
|
|
12603
|
+
if (fs27.existsSync(fullPath) && fs27.statSync(fullPath).isFile()) {
|
|
12604
|
+
const content = fs27.readFileSync(fullPath, "utf-8");
|
|
12238
12605
|
const lines = content.split("\n");
|
|
12239
12606
|
const findings = [];
|
|
12240
12607
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -12288,23 +12655,23 @@ async function handleGitignore(_params) {
|
|
|
12288
12655
|
}
|
|
12289
12656
|
async function handleClean(_params) {
|
|
12290
12657
|
sessionMemory.recordToolCall("kuma_safety_clean", {});
|
|
12291
|
-
const
|
|
12292
|
-
const
|
|
12658
|
+
const fs27 = await import("fs");
|
|
12659
|
+
const path27 = await import("path");
|
|
12293
12660
|
const root = process.cwd();
|
|
12294
|
-
const scratchDir =
|
|
12661
|
+
const scratchDir = path27.resolve(root, ".kuma", "scratch");
|
|
12295
12662
|
let removed = 0;
|
|
12296
|
-
if (
|
|
12663
|
+
if (fs27.existsSync(scratchDir)) {
|
|
12297
12664
|
try {
|
|
12298
|
-
const entries =
|
|
12665
|
+
const entries = fs27.readdirSync(scratchDir);
|
|
12299
12666
|
for (const entry of entries) {
|
|
12300
|
-
const fullPath =
|
|
12667
|
+
const fullPath = path27.join(scratchDir, entry);
|
|
12301
12668
|
try {
|
|
12302
|
-
const stat =
|
|
12669
|
+
const stat = fs27.statSync(fullPath);
|
|
12303
12670
|
if (stat.isFile()) {
|
|
12304
|
-
|
|
12671
|
+
fs27.unlinkSync(fullPath);
|
|
12305
12672
|
removed++;
|
|
12306
12673
|
} else if (stat.isDirectory()) {
|
|
12307
|
-
|
|
12674
|
+
fs27.rmSync(fullPath, { recursive: true, force: true });
|
|
12308
12675
|
removed++;
|
|
12309
12676
|
}
|
|
12310
12677
|
} catch {
|
|
@@ -12432,6 +12799,64 @@ async function handleVerify(params) {
|
|
|
12432
12799
|
return verifyResult + recordingWarning;
|
|
12433
12800
|
}
|
|
12434
12801
|
|
|
12802
|
+
// src/engine/kumaAutoInit.ts
|
|
12803
|
+
init_sessionMemory();
|
|
12804
|
+
var _initialized = false;
|
|
12805
|
+
var _initPromise = null;
|
|
12806
|
+
async function ensureInitialized() {
|
|
12807
|
+
if (_initialized) return false;
|
|
12808
|
+
if (_initPromise) {
|
|
12809
|
+
await _initPromise;
|
|
12810
|
+
return false;
|
|
12811
|
+
}
|
|
12812
|
+
_initPromise = doInit();
|
|
12813
|
+
await _initPromise;
|
|
12814
|
+
return true;
|
|
12815
|
+
}
|
|
12816
|
+
async function doInit() {
|
|
12817
|
+
try {
|
|
12818
|
+
sessionMemory.init({
|
|
12819
|
+
projectRoot: process.cwd(),
|
|
12820
|
+
startTime: Date.now()
|
|
12821
|
+
});
|
|
12822
|
+
try {
|
|
12823
|
+
const sessionInfo = sessionMemory.loadSession();
|
|
12824
|
+
if (sessionInfo.hasPrevSession) {
|
|
12825
|
+
console.error(`[kuma:auto-init] \u2705 Restored session (${sessionInfo.toolCallCount} previous tool calls)`);
|
|
12826
|
+
}
|
|
12827
|
+
} catch {
|
|
12828
|
+
}
|
|
12829
|
+
try {
|
|
12830
|
+
const { buildFromSessionMemory: buildFromSessionMemory2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
|
|
12831
|
+
const edgeCount = await buildFromSessionMemory2();
|
|
12832
|
+
if (edgeCount > 0) {
|
|
12833
|
+
console.error(`[kuma:auto-init] \u2705 Graph auto-populated with ${edgeCount} entries`);
|
|
12834
|
+
}
|
|
12835
|
+
} catch {
|
|
12836
|
+
}
|
|
12837
|
+
try {
|
|
12838
|
+
const { buildSearchVectors: buildSearchVectors2 } = await Promise.resolve().then(() => (init_kumaSearch(), kumaSearch_exports));
|
|
12839
|
+
await buildSearchVectors2();
|
|
12840
|
+
} catch {
|
|
12841
|
+
}
|
|
12842
|
+
try {
|
|
12843
|
+
const { getDb: getDb2, saveDb: saveDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
12844
|
+
const db = await getDb2();
|
|
12845
|
+
db.run(
|
|
12846
|
+
`INSERT INTO sessions (started_at, goal, tool_calls) VALUES (?, ?, ?)`,
|
|
12847
|
+
[Math.floor(Date.now() / 1e3), "Auto-initialized", 0]
|
|
12848
|
+
);
|
|
12849
|
+
saveDb2(db);
|
|
12850
|
+
} catch {
|
|
12851
|
+
}
|
|
12852
|
+
_initialized = true;
|
|
12853
|
+
console.error(`[kuma:auto-init] \u2705 Zero-call auto-init complete`);
|
|
12854
|
+
} catch (err) {
|
|
12855
|
+
console.error(`[kuma:auto-init] \u26A0\uFE0F Auto-init error: ${err}`);
|
|
12856
|
+
_initialized = true;
|
|
12857
|
+
}
|
|
12858
|
+
}
|
|
12859
|
+
|
|
12435
12860
|
// src/manifest.ts
|
|
12436
12861
|
function registerAllTools(server) {
|
|
12437
12862
|
server.tool(
|
|
@@ -12448,6 +12873,7 @@ function registerAllTools(server) {
|
|
|
12448
12873
|
},
|
|
12449
12874
|
async (params) => {
|
|
12450
12875
|
try {
|
|
12876
|
+
await ensureInitialized();
|
|
12451
12877
|
const text = await handleContext({
|
|
12452
12878
|
action: params.action,
|
|
12453
12879
|
scope: params.scope,
|
|
@@ -12504,6 +12930,7 @@ function registerAllTools(server) {
|
|
|
12504
12930
|
},
|
|
12505
12931
|
async (params) => {
|
|
12506
12932
|
try {
|
|
12933
|
+
await ensureInitialized();
|
|
12507
12934
|
const text = await handleMemory({
|
|
12508
12935
|
action: params.action,
|
|
12509
12936
|
scope: params.scope,
|
|
@@ -12563,6 +12990,7 @@ function registerAllTools(server) {
|
|
|
12563
12990
|
},
|
|
12564
12991
|
async (params) => {
|
|
12565
12992
|
try {
|
|
12993
|
+
await ensureInitialized();
|
|
12566
12994
|
const text = await handleSafety({
|
|
12567
12995
|
action: params.action,
|
|
12568
12996
|
guardGoal: params.guardGoal,
|
|
@@ -12590,6 +13018,8 @@ function registerAllTools(server) {
|
|
|
12590
13018
|
}
|
|
12591
13019
|
);
|
|
12592
13020
|
console.error("[Manifest] Registered 3 V3 coarse-grained tools (kuma_context, kuma_memory, kuma_safety).");
|
|
13021
|
+
console.error("[Manifest] Pilar 1: Auto-init hooks installed on all tools.");
|
|
13022
|
+
console.error("[Manifest] Pilar 2: Namespace aliases active (kuma_kuma_* \u2192 kuma_*, context \u2192 kuma_context).");
|
|
12593
13023
|
}
|
|
12594
13024
|
|
|
12595
13025
|
// src/index.ts
|
|
@@ -12655,14 +13085,14 @@ async function main() {
|
|
|
12655
13085
|
} catch {
|
|
12656
13086
|
}
|
|
12657
13087
|
try {
|
|
12658
|
-
const
|
|
12659
|
-
const
|
|
12660
|
-
const lockDir =
|
|
12661
|
-
if (
|
|
12662
|
-
const pidFile =
|
|
12663
|
-
if (
|
|
13088
|
+
const path27 = await import("path");
|
|
13089
|
+
const fs27 = await import("fs");
|
|
13090
|
+
const lockDir = path27.resolve(process.cwd(), ".kuma/verifier.lock");
|
|
13091
|
+
if (fs27.existsSync(lockDir)) {
|
|
13092
|
+
const pidFile = path27.join(lockDir, "pid");
|
|
13093
|
+
if (fs27.existsSync(pidFile)) {
|
|
12664
13094
|
try {
|
|
12665
|
-
const pid = parseInt(
|
|
13095
|
+
const pid = parseInt(fs27.readFileSync(pidFile, "utf-8"), 10);
|
|
12666
13096
|
if (pid && pid !== process.pid) {
|
|
12667
13097
|
try {
|
|
12668
13098
|
process.kill(-pid, "SIGKILL");
|
|
@@ -12678,7 +13108,7 @@ async function main() {
|
|
|
12678
13108
|
} catch {
|
|
12679
13109
|
}
|
|
12680
13110
|
}
|
|
12681
|
-
|
|
13111
|
+
fs27.rmSync(lockDir, { recursive: true, force: true });
|
|
12682
13112
|
}
|
|
12683
13113
|
if (killedCount === 0) {
|
|
12684
13114
|
console.error("\u2705 No running verifications found to kill.");
|
|
@@ -12686,25 +13116,25 @@ async function main() {
|
|
|
12686
13116
|
} catch {
|
|
12687
13117
|
}
|
|
12688
13118
|
try {
|
|
12689
|
-
const { execSync:
|
|
13119
|
+
const { execSync: execSync8 } = await import("child_process");
|
|
12690
13120
|
try {
|
|
12691
|
-
|
|
13121
|
+
execSync8("pkill -f 'jest' 2>/dev/null || true");
|
|
12692
13122
|
} catch {
|
|
12693
13123
|
}
|
|
12694
13124
|
try {
|
|
12695
|
-
|
|
13125
|
+
execSync8("pkill -f 'jest-worker' 2>/dev/null || true");
|
|
12696
13126
|
} catch {
|
|
12697
13127
|
}
|
|
12698
13128
|
try {
|
|
12699
|
-
|
|
13129
|
+
execSync8("pkill -f 'pnpm test' 2>/dev/null || true");
|
|
12700
13130
|
} catch {
|
|
12701
13131
|
}
|
|
12702
13132
|
try {
|
|
12703
|
-
|
|
13133
|
+
execSync8("pkill -f 'npm test' 2>/dev/null || true");
|
|
12704
13134
|
} catch {
|
|
12705
13135
|
}
|
|
12706
13136
|
try {
|
|
12707
|
-
|
|
13137
|
+
execSync8("pkill -f 'yarn test' 2>/dev/null || true");
|
|
12708
13138
|
} catch {
|
|
12709
13139
|
}
|
|
12710
13140
|
console.error("\u2705 Killed orphaned test processes");
|
|
@@ -12756,6 +13186,14 @@ async function main() {
|
|
|
12756
13186
|
return;
|
|
12757
13187
|
}
|
|
12758
13188
|
if (args[0] === "--hook") {
|
|
13189
|
+
const hookType = args[1] || "post-commit";
|
|
13190
|
+
if (hookType === "post-commit") {
|
|
13191
|
+
try {
|
|
13192
|
+
const { harvestGitHistory: harvestGitHistory2 } = await Promise.resolve().then(() => (init_kumaGitHarvester(), kumaGitHarvester_exports));
|
|
13193
|
+
await harvestGitHistory2({ commitCount: 1 });
|
|
13194
|
+
} catch {
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
12759
13197
|
process.exit(0);
|
|
12760
13198
|
}
|
|
12761
13199
|
if (args[0] === "init") {
|
|
@@ -12818,23 +13256,23 @@ async function main() {
|
|
|
12818
13256
|
});
|
|
12819
13257
|
const output = formatInitResults(results);
|
|
12820
13258
|
console.log(output);
|
|
12821
|
-
const
|
|
12822
|
-
const
|
|
12823
|
-
const matchaSkills =
|
|
12824
|
-
const matchaAgents =
|
|
13259
|
+
const fs27 = await import("fs");
|
|
13260
|
+
const path27 = await import("path");
|
|
13261
|
+
const matchaSkills = path27.resolve(process.cwd(), "skills/matcha/SKILL.md");
|
|
13262
|
+
const matchaAgents = path27.resolve(
|
|
12825
13263
|
process.cwd(),
|
|
12826
13264
|
".agents/skills/matcha/SKILL.md"
|
|
12827
13265
|
);
|
|
12828
|
-
const matchaRootSkills =
|
|
13266
|
+
const matchaRootSkills = path27.resolve(
|
|
12829
13267
|
process.cwd(),
|
|
12830
13268
|
"skills/matcha/SKILL.md"
|
|
12831
13269
|
);
|
|
12832
|
-
const matchaAgentsMd =
|
|
12833
|
-
const matchaWindsurfRules =
|
|
13270
|
+
const matchaAgentsMd = path27.resolve(process.cwd(), "AGENTS.md");
|
|
13271
|
+
const matchaWindsurfRules = path27.resolve(
|
|
12834
13272
|
process.cwd(),
|
|
12835
13273
|
".windsurf"
|
|
12836
13274
|
);
|
|
12837
|
-
if (
|
|
13275
|
+
if (fs27.existsSync(matchaSkills) || fs27.existsSync(matchaAgents) || fs27.existsSync(matchaRootSkills) || fs27.existsSync(matchaAgentsMd) || fs27.existsSync(matchaWindsurfRules)) {
|
|
12838
13276
|
console.error(
|
|
12839
13277
|
"\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!"
|
|
12840
13278
|
);
|
|
@@ -12848,13 +13286,13 @@ async function main() {
|
|
|
12848
13286
|
(async () => {
|
|
12849
13287
|
try {
|
|
12850
13288
|
const { generateInitMdContent: generateInitMdContent2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
12851
|
-
const
|
|
12852
|
-
const
|
|
12853
|
-
const initMdPath =
|
|
12854
|
-
if (!
|
|
12855
|
-
const kumaDir =
|
|
12856
|
-
if (!
|
|
12857
|
-
|
|
13289
|
+
const fs27 = await import("fs");
|
|
13290
|
+
const path27 = await import("path");
|
|
13291
|
+
const initMdPath = path27.resolve(process.cwd(), ".kuma/init.md");
|
|
13292
|
+
if (!fs27.existsSync(initMdPath)) {
|
|
13293
|
+
const kumaDir = path27.dirname(initMdPath);
|
|
13294
|
+
if (!fs27.existsSync(kumaDir)) fs27.mkdirSync(kumaDir, { recursive: true });
|
|
13295
|
+
fs27.writeFileSync(initMdPath, generateInitMdContent2(), "utf-8");
|
|
12858
13296
|
console.error(`[${SERVER_NAME}] Auto-generated .kuma/init.md`);
|
|
12859
13297
|
}
|
|
12860
13298
|
} catch (err) {
|
|
@@ -12865,8 +13303,8 @@ async function main() {
|
|
|
12865
13303
|
try {
|
|
12866
13304
|
const { detectAgent: detectAgent2, getSkillPath: getSkillPath2, getAgentLabel: getAgentLabel2 } = await Promise.resolve().then(() => (init_agentDetector(), agentDetector_exports));
|
|
12867
13305
|
const { generateSkill: generateSkill2, getSecondaryFiles: getSecondaryFiles2 } = await Promise.resolve().then(() => (init_skillGenerator(), skillGenerator_exports));
|
|
12868
|
-
const
|
|
12869
|
-
const
|
|
13306
|
+
const fs27 = await import("fs");
|
|
13307
|
+
const path27 = await import("path");
|
|
12870
13308
|
const detection = detectAgent2();
|
|
12871
13309
|
if (!detection.primary) {
|
|
12872
13310
|
console.error(`[${SERVER_NAME}] No AI agent detected \u2014 skipping auto-skill creation`);
|
|
@@ -12874,22 +13312,22 @@ async function main() {
|
|
|
12874
13312
|
}
|
|
12875
13313
|
const agentType = detection.primary;
|
|
12876
13314
|
const skillPath = getSkillPath2(agentType);
|
|
12877
|
-
const fullPath =
|
|
12878
|
-
if (
|
|
13315
|
+
const fullPath = path27.resolve(process.cwd(), skillPath);
|
|
13316
|
+
if (fs27.existsSync(fullPath)) {
|
|
12879
13317
|
console.error(`[${SERVER_NAME}] Skill exists for ${getAgentLabel2(agentType)} \u2014 skipping`);
|
|
12880
13318
|
return;
|
|
12881
13319
|
}
|
|
12882
|
-
const dir =
|
|
12883
|
-
if (!
|
|
12884
|
-
|
|
13320
|
+
const dir = path27.dirname(fullPath);
|
|
13321
|
+
if (!fs27.existsSync(dir)) fs27.mkdirSync(dir, { recursive: true });
|
|
13322
|
+
fs27.writeFileSync(fullPath, generateSkill2(agentType), "utf-8");
|
|
12885
13323
|
console.error(`[${SERVER_NAME}] Auto-created ${skillPath} for ${getAgentLabel2(agentType)}`);
|
|
12886
13324
|
const secondaryFiles = getSecondaryFiles2(agentType);
|
|
12887
13325
|
for (const sf of secondaryFiles) {
|
|
12888
|
-
const sfPath =
|
|
12889
|
-
const sfDir =
|
|
12890
|
-
if (!
|
|
12891
|
-
if (!
|
|
12892
|
-
|
|
13326
|
+
const sfPath = path27.resolve(process.cwd(), sf.path);
|
|
13327
|
+
const sfDir = path27.dirname(sfPath);
|
|
13328
|
+
if (!fs27.existsSync(sfPath)) {
|
|
13329
|
+
if (!fs27.existsSync(sfDir)) fs27.mkdirSync(sfDir, { recursive: true });
|
|
13330
|
+
fs27.writeFileSync(sfPath, sf.content, "utf-8");
|
|
12893
13331
|
console.error(`[${SERVER_NAME}] Auto-created ${sf.path}`);
|
|
12894
13332
|
}
|
|
12895
13333
|
}
|
|
@@ -12914,11 +13352,11 @@ async function main() {
|
|
|
12914
13352
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Graph auto-population: ${err}`);
|
|
12915
13353
|
}
|
|
12916
13354
|
try {
|
|
12917
|
-
const
|
|
12918
|
-
const
|
|
12919
|
-
const scratchDir =
|
|
12920
|
-
if (!
|
|
12921
|
-
|
|
13355
|
+
const fs27 = await import("fs");
|
|
13356
|
+
const path27 = await import("path");
|
|
13357
|
+
const scratchDir = path27.resolve(process.cwd(), ".kuma", "scratch");
|
|
13358
|
+
if (!fs27.existsSync(scratchDir)) {
|
|
13359
|
+
fs27.mkdirSync(scratchDir, { recursive: true });
|
|
12922
13360
|
console.error(`[${SERVER_NAME}] \u2705 Created .kuma/scratch/ for temporary debug artifacts`);
|
|
12923
13361
|
}
|
|
12924
13362
|
} catch (err) {
|
|
@@ -12936,33 +13374,11 @@ async function main() {
|
|
|
12936
13374
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Session DB record: ${err}`);
|
|
12937
13375
|
}
|
|
12938
13376
|
try {
|
|
12939
|
-
const
|
|
12940
|
-
const
|
|
12941
|
-
|
|
12942
|
-
if (!fs26.existsSync(hooksDir)) {
|
|
12943
|
-
fs26.mkdirSync(hooksDir, { recursive: true });
|
|
12944
|
-
const hookContent = `#!/bin/bash
|
|
12945
|
-
# Kuma auto-capture hook (post-commit)
|
|
12946
|
-
# Auto-updates knowledge graph after git commits
|
|
12947
|
-
if command -v npx &> /dev/null; then
|
|
12948
|
-
npx -y @plumpslabs/kuma --hook post-commit 2>/dev/null || true
|
|
12949
|
-
fi
|
|
12950
|
-
`;
|
|
12951
|
-
const gitHooksDir = path26.resolve(process.cwd(), ".git", "hooks");
|
|
12952
|
-
if (fs26.existsSync(gitHooksDir)) {
|
|
12953
|
-
const postCommitPath = path26.join(gitHooksDir, "post-commit");
|
|
12954
|
-
if (!fs26.existsSync(postCommitPath)) {
|
|
12955
|
-
fs26.writeFileSync(postCommitPath, hookContent, "utf-8");
|
|
12956
|
-
try {
|
|
12957
|
-
fs26.chmodSync(postCommitPath, 493);
|
|
12958
|
-
} catch {
|
|
12959
|
-
}
|
|
12960
|
-
console.error(`[${SERVER_NAME}] \u2705 Created .git/hooks/post-commit for auto-capture`);
|
|
12961
|
-
}
|
|
12962
|
-
}
|
|
12963
|
-
}
|
|
13377
|
+
const { installGitHook: installGitHook2 } = await Promise.resolve().then(() => (init_kumaGitHarvester(), kumaGitHarvester_exports));
|
|
13378
|
+
const hookResult = installGitHook2();
|
|
13379
|
+
console.error(`[${SERVER_NAME}] ${hookResult}`);
|
|
12964
13380
|
} catch (err) {
|
|
12965
|
-
console.error(`[${SERVER_NAME}] \u26A0\uFE0F
|
|
13381
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Git hook setup: ${err}`);
|
|
12966
13382
|
}
|
|
12967
13383
|
try {
|
|
12968
13384
|
const { buildSearchVectors: buildSearchVectors2 } = await Promise.resolve().then(() => (init_kumaSearch(), kumaSearch_exports));
|
|
@@ -12973,6 +13389,13 @@ fi
|
|
|
12973
13389
|
} catch (err) {
|
|
12974
13390
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Search vector cache: ${err}`);
|
|
12975
13391
|
}
|
|
13392
|
+
try {
|
|
13393
|
+
const { buildGraphConnectivity: buildGraphConnectivity2 } = await Promise.resolve().then(() => (init_kumaSearch(), kumaSearch_exports));
|
|
13394
|
+
await buildGraphConnectivity2();
|
|
13395
|
+
console.error(`[${SERVER_NAME}] \u2705 Graph connectivity index built`);
|
|
13396
|
+
} catch (err) {
|
|
13397
|
+
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Graph connectivity: ${err}`);
|
|
13398
|
+
}
|
|
12976
13399
|
console.error(`[${SERVER_NAME}] \u2705 Cold start bootstrap complete`);
|
|
12977
13400
|
} catch (err) {
|
|
12978
13401
|
console.error(`[${SERVER_NAME}] \u26A0\uFE0F Cold start bootstrap error: ${err}`);
|
|
@@ -12998,13 +13421,19 @@ fi
|
|
|
12998
13421
|
`[${SERVER_NAME}] Session started: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
12999
13422
|
);
|
|
13000
13423
|
console.error(
|
|
13001
|
-
`[${SERVER_NAME}] Kuma
|
|
13424
|
+
`[${SERVER_NAME}] Kuma v2.0 \u2014 Zero-Overhead Autonomous Brain Engine`
|
|
13425
|
+
);
|
|
13426
|
+
console.error(
|
|
13427
|
+
`[${SERVER_NAME}] \u26A1 Auto-init: no manual init needed (Pilar 1)`
|
|
13428
|
+
);
|
|
13429
|
+
console.error(
|
|
13430
|
+
`[${SERVER_NAME}] \u{1F4DB} Namespace normalized: kuma_kuma_* \u2192 kuma_* (Pilar 2)`
|
|
13002
13431
|
);
|
|
13003
13432
|
console.error(
|
|
13004
|
-
`[${SERVER_NAME}] \u{
|
|
13433
|
+
`[${SERVER_NAME}] \u{1F9F9} Noise filter: AST node types rejected (Pilar 4)`
|
|
13005
13434
|
);
|
|
13006
13435
|
console.error(
|
|
13007
|
-
`[${SERVER_NAME}] \u{
|
|
13436
|
+
`[${SERVER_NAME}] \u{1F419} Git auto-harvester installed (Pilar 5)`
|
|
13008
13437
|
);
|
|
13009
13438
|
console.error(
|
|
13010
13439
|
`[${SERVER_NAME}] \u{1F6E1}\uFE0F 3 coarse-grained tools: kuma_context, kuma_memory, kuma_safety`
|