@plumpslabs/kuma 2.4.7 → 2.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +164 -111
- package/hooks/kuma-agy-hooks.js +28 -3
- package/package.json +2 -2
- package/plugin.json +1 -1
package/dist/index.js
CHANGED
|
@@ -258,9 +258,23 @@ function generateNodeId(type, name) {
|
|
|
258
258
|
function resetDbInstance() {
|
|
259
259
|
dbInstance = null;
|
|
260
260
|
initPromise = null;
|
|
261
|
+
lastLoadedMtime = 0;
|
|
261
262
|
}
|
|
262
263
|
async function getDb() {
|
|
263
|
-
if (dbInstance)
|
|
264
|
+
if (dbInstance) {
|
|
265
|
+
try {
|
|
266
|
+
const kumaDir = getKumaDir();
|
|
267
|
+
const dbPath = path2.join(kumaDir, DB_FILENAME);
|
|
268
|
+
if (fs2.existsSync(dbPath)) {
|
|
269
|
+
const stat = fs2.statSync(dbPath);
|
|
270
|
+
if (stat.mtimeMs > lastLoadedMtime + 50) {
|
|
271
|
+
resetDbInstance();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
if (dbInstance) return dbInstance;
|
|
277
|
+
}
|
|
264
278
|
if (initPromise) return initPromise;
|
|
265
279
|
initPromise = initDb();
|
|
266
280
|
return initPromise;
|
|
@@ -274,6 +288,10 @@ async function initDb() {
|
|
|
274
288
|
}
|
|
275
289
|
let db;
|
|
276
290
|
if (fs2.existsSync(dbPath)) {
|
|
291
|
+
try {
|
|
292
|
+
lastLoadedMtime = fs2.statSync(dbPath).mtimeMs;
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
277
295
|
const buffer = fs2.readFileSync(dbPath);
|
|
278
296
|
db = new SQL.Database(buffer);
|
|
279
297
|
} else {
|
|
@@ -309,8 +327,16 @@ function flushDb(db) {
|
|
|
309
327
|
fs2.writeFileSync(tmpPath, buffer);
|
|
310
328
|
try {
|
|
311
329
|
fs2.renameSync(tmpPath, dbPath);
|
|
330
|
+
try {
|
|
331
|
+
lastLoadedMtime = fs2.statSync(dbPath).mtimeMs;
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
312
334
|
} catch {
|
|
313
335
|
fs2.writeFileSync(dbPath, buffer);
|
|
336
|
+
try {
|
|
337
|
+
lastLoadedMtime = fs2.statSync(dbPath).mtimeMs;
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
314
340
|
try {
|
|
315
341
|
if (fs2.existsSync(tmpPath)) fs2.unlinkSync(tmpPath);
|
|
316
342
|
} catch {
|
|
@@ -1146,8 +1172,8 @@ async function runDoctor() {
|
|
|
1146
1172
|
} catch {
|
|
1147
1173
|
}
|
|
1148
1174
|
try {
|
|
1149
|
-
const { execSync:
|
|
1150
|
-
const psOutput =
|
|
1175
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
1176
|
+
const psOutput = execSync7(
|
|
1151
1177
|
`ps -eo pid,ppid,command | grep -E "(jest|pnpm test|npm test|yarn test)" | grep -v grep | head -10`,
|
|
1152
1178
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
1153
1179
|
).trim();
|
|
@@ -1265,7 +1291,7 @@ function rebuildFtsIndex() {
|
|
|
1265
1291
|
} catch {
|
|
1266
1292
|
}
|
|
1267
1293
|
}
|
|
1268
|
-
var DB_FILENAME, dbInstance, initPromise, _saveTimeout, _pendingDb, SAVE_DEBOUNCE_MS;
|
|
1294
|
+
var DB_FILENAME, dbInstance, initPromise, lastLoadedMtime, _saveTimeout, _pendingDb, SAVE_DEBOUNCE_MS;
|
|
1269
1295
|
var init_kumaDb = __esm({
|
|
1270
1296
|
"src/engine/kumaDb.ts"() {
|
|
1271
1297
|
"use strict";
|
|
@@ -1273,6 +1299,7 @@ var init_kumaDb = __esm({
|
|
|
1273
1299
|
DB_FILENAME = "kuma.db";
|
|
1274
1300
|
dbInstance = null;
|
|
1275
1301
|
initPromise = null;
|
|
1302
|
+
lastLoadedMtime = 0;
|
|
1276
1303
|
_saveTimeout = null;
|
|
1277
1304
|
_pendingDb = null;
|
|
1278
1305
|
SAVE_DEBOUNCE_MS = 100;
|
|
@@ -2556,6 +2583,8 @@ var init_languageSupport = __esm({
|
|
|
2556
2583
|
// src/engine/kumaDriftDetector.ts
|
|
2557
2584
|
var kumaDriftDetector_exports = {};
|
|
2558
2585
|
__export(kumaDriftDetector_exports, {
|
|
2586
|
+
computeProjectHash: () => computeProjectHash,
|
|
2587
|
+
computeScopeHash: () => computeScopeHash,
|
|
2559
2588
|
detectDrift: () => detectDrift,
|
|
2560
2589
|
flagStaleRecords: () => flagStaleRecords,
|
|
2561
2590
|
formatDriftReport: () => formatDriftReport,
|
|
@@ -2565,6 +2594,7 @@ __export(kumaDriftDetector_exports, {
|
|
|
2565
2594
|
import fs5 from "fs";
|
|
2566
2595
|
import path6 from "path";
|
|
2567
2596
|
import crypto2 from "crypto";
|
|
2597
|
+
import { execSync as execSync3 } from "child_process";
|
|
2568
2598
|
function hashFile(filePath) {
|
|
2569
2599
|
try {
|
|
2570
2600
|
const root = getProjectRoot();
|
|
@@ -2644,34 +2674,70 @@ async function detectDrift() {
|
|
|
2644
2674
|
}
|
|
2645
2675
|
function computeScopeHash(scope) {
|
|
2646
2676
|
try {
|
|
2647
|
-
const hash = crypto2.createHash("sha256");
|
|
2648
2677
|
const root = getProjectRoot();
|
|
2678
|
+
let gitHead = "";
|
|
2679
|
+
let gitStatus = "";
|
|
2680
|
+
try {
|
|
2681
|
+
gitHead = execSync3("git rev-parse HEAD", {
|
|
2682
|
+
cwd: root,
|
|
2683
|
+
encoding: "utf-8",
|
|
2684
|
+
timeout: 1500,
|
|
2685
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2686
|
+
}).trim();
|
|
2687
|
+
gitStatus = execSync3("git status --porcelain", {
|
|
2688
|
+
cwd: root,
|
|
2689
|
+
encoding: "utf-8",
|
|
2690
|
+
timeout: 2e3,
|
|
2691
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2692
|
+
}).trim();
|
|
2693
|
+
} catch {
|
|
2694
|
+
}
|
|
2695
|
+
const hash = crypto2.createHash("sha256");
|
|
2649
2696
|
hash.update(scope);
|
|
2650
2697
|
let found = false;
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2698
|
+
if (gitHead || gitStatus) {
|
|
2699
|
+
hash.update(gitHead);
|
|
2700
|
+
hash.update(gitStatus);
|
|
2701
|
+
found = true;
|
|
2702
|
+
} else {
|
|
2703
|
+
const checkDirs = [
|
|
2704
|
+
root,
|
|
2705
|
+
path6.join(root, "src"),
|
|
2706
|
+
path6.join(root, "lib"),
|
|
2707
|
+
path6.join(root, "backend"),
|
|
2708
|
+
path6.join(root, "packages"),
|
|
2709
|
+
path6.join(root, "app")
|
|
2710
|
+
];
|
|
2711
|
+
for (const dir of checkDirs) {
|
|
2712
|
+
if (fs5.existsSync(dir)) {
|
|
2655
2713
|
try {
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2714
|
+
const files = fs5.readdirSync(dir).slice(0, 30);
|
|
2715
|
+
for (const f of files) {
|
|
2716
|
+
try {
|
|
2717
|
+
const fullPath = path6.join(dir, f);
|
|
2718
|
+
const stat = fs5.statSync(fullPath);
|
|
2719
|
+
if (stat.isFile()) {
|
|
2720
|
+
hash.update(`${f}:${stat.mtimeMs}`);
|
|
2721
|
+
found = true;
|
|
2722
|
+
}
|
|
2723
|
+
} catch {
|
|
2724
|
+
}
|
|
2661
2725
|
}
|
|
2662
2726
|
} catch {
|
|
2663
2727
|
}
|
|
2664
2728
|
}
|
|
2665
2729
|
}
|
|
2666
|
-
} catch {
|
|
2667
2730
|
}
|
|
2668
2731
|
const researchDir = path6.join(root, ".kuma", "research");
|
|
2669
2732
|
if (fs5.existsSync(researchDir)) {
|
|
2670
2733
|
const scopeFile = path6.join(researchDir, `${scope}.json`);
|
|
2671
2734
|
if (fs5.existsSync(scopeFile)) {
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2735
|
+
try {
|
|
2736
|
+
const content = fs5.readFileSync(scopeFile, "utf-8");
|
|
2737
|
+
hash.update(content);
|
|
2738
|
+
found = true;
|
|
2739
|
+
} catch {
|
|
2740
|
+
}
|
|
2675
2741
|
}
|
|
2676
2742
|
}
|
|
2677
2743
|
if (!found) return null;
|
|
@@ -2744,11 +2810,13 @@ async function getDriftSummary() {
|
|
|
2744
2810
|
if (staleCount === 0) return "\u2705 All records fresh";
|
|
2745
2811
|
return `\u26A0\uFE0F ${staleCount} stale record(s) detected. Use kuma_memory({ action: 'heal' }) to repair.`;
|
|
2746
2812
|
}
|
|
2813
|
+
var computeProjectHash;
|
|
2747
2814
|
var init_kumaDriftDetector = __esm({
|
|
2748
2815
|
"src/engine/kumaDriftDetector.ts"() {
|
|
2749
2816
|
"use strict";
|
|
2750
2817
|
init_kumaDb();
|
|
2751
2818
|
init_pathValidator();
|
|
2819
|
+
computeProjectHash = (scope) => computeScopeHash(scope) || Date.now().toString(16);
|
|
2752
2820
|
}
|
|
2753
2821
|
});
|
|
2754
2822
|
|
|
@@ -4342,9 +4410,9 @@ async function analyzeImpact(target) {
|
|
|
4342
4410
|
}
|
|
4343
4411
|
async function codebaseImpactFallback(target) {
|
|
4344
4412
|
try {
|
|
4345
|
-
const { execSync:
|
|
4413
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
4346
4414
|
const root = process.cwd();
|
|
4347
|
-
const grepResult =
|
|
4415
|
+
const grepResult = execSync7(
|
|
4348
4416
|
`grep -rn ${grepIncludeFlags()} --include="*.json" --include="*.md" -l "${target.replace(/"/g, '\\"')}" . 2>/dev/null | grep -v node_modules | grep -v .git | grep -v dist | grep -v .kuma | head -50`,
|
|
4349
4417
|
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
4350
4418
|
).trim();
|
|
@@ -4471,7 +4539,7 @@ var init_kumaGraph = __esm({
|
|
|
4471
4539
|
});
|
|
4472
4540
|
|
|
4473
4541
|
// src/utils/kumaShared.ts
|
|
4474
|
-
import { execSync as
|
|
4542
|
+
import { execSync as execSync4 } from "child_process";
|
|
4475
4543
|
function isEditTool(toolName) {
|
|
4476
4544
|
const n = (toolName || "").toLowerCase();
|
|
4477
4545
|
return n.includes("edit") || n.includes("write") || n.includes("patch") || n.includes("apply") || n.includes("replace");
|
|
@@ -4509,7 +4577,7 @@ function getSessionStats(inputGoal) {
|
|
|
4509
4577
|
function getGitDiffStat(timeout = 3e3) {
|
|
4510
4578
|
try {
|
|
4511
4579
|
const root = getProjectRoot();
|
|
4512
|
-
return
|
|
4580
|
+
return execSync4("git diff --stat", {
|
|
4513
4581
|
cwd: root,
|
|
4514
4582
|
encoding: "utf-8",
|
|
4515
4583
|
timeout
|
|
@@ -5656,39 +5724,8 @@ async function syncGotchasGraph() {
|
|
|
5656
5724
|
return { created: 0 };
|
|
5657
5725
|
}
|
|
5658
5726
|
}
|
|
5659
|
-
async function resolveGotchasForScope(
|
|
5660
|
-
|
|
5661
|
-
await ensureGotchasSchema();
|
|
5662
|
-
const db = await getDb();
|
|
5663
|
-
const stmt = db.prepare(
|
|
5664
|
-
`SELECT id, file_path, content_hash FROM known_gotchas
|
|
5665
|
-
WHERE status = 'active' AND (file_path LIKE ? OR file_path = ?) AND content_hash IS NOT NULL`
|
|
5666
|
-
);
|
|
5667
|
-
stmt.bind([`%${scope}%`, scope]);
|
|
5668
|
-
const rows = [];
|
|
5669
|
-
while (stmt.step()) rows.push(stmt.getAsObject());
|
|
5670
|
-
stmt.free();
|
|
5671
|
-
let resolved = 0;
|
|
5672
|
-
const resolvedPaths = [];
|
|
5673
|
-
for (const r of rows) {
|
|
5674
|
-
const current = hashFileContent(r.file_path);
|
|
5675
|
-
if (current && current !== r.content_hash) {
|
|
5676
|
-
db.run(
|
|
5677
|
-
`UPDATE known_gotchas SET status = 'resolved', last_verified_at = strftime('%s','now') WHERE id = ?`,
|
|
5678
|
-
[r.id]
|
|
5679
|
-
);
|
|
5680
|
-
resolved++;
|
|
5681
|
-
resolvedPaths.push(r.file_path);
|
|
5682
|
-
}
|
|
5683
|
-
}
|
|
5684
|
-
if (resolved > 0) {
|
|
5685
|
-
saveDb();
|
|
5686
|
-
syncGotchaStatusToMarkdown([scope, ...resolvedPaths], "resolved", "auto-verified");
|
|
5687
|
-
}
|
|
5688
|
-
return { resolved };
|
|
5689
|
-
} catch {
|
|
5690
|
-
return { resolved: 0 };
|
|
5691
|
-
}
|
|
5727
|
+
async function resolveGotchasForScope(_scope) {
|
|
5728
|
+
return { resolved: 0 };
|
|
5692
5729
|
}
|
|
5693
5730
|
async function getActiveGotchasForCommand(command) {
|
|
5694
5731
|
try {
|
|
@@ -7502,10 +7539,29 @@ async function createCheckpoint(label, description) {
|
|
|
7502
7539
|
return `\u26A0\uFE0F Checkpoint "${label}" already exists. Use a different label or remove it first.`;
|
|
7503
7540
|
}
|
|
7504
7541
|
fs22.mkdirSync(cpDir, { recursive: true });
|
|
7542
|
+
let gitHead;
|
|
7543
|
+
let isGitClean = false;
|
|
7544
|
+
try {
|
|
7545
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
7546
|
+
gitHead = execSync7("git rev-parse HEAD", {
|
|
7547
|
+
cwd: root,
|
|
7548
|
+
encoding: "utf-8",
|
|
7549
|
+
timeout: 3e3,
|
|
7550
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7551
|
+
}).trim();
|
|
7552
|
+
const statusOut = execSync7("git status --porcelain", {
|
|
7553
|
+
cwd: root,
|
|
7554
|
+
encoding: "utf-8",
|
|
7555
|
+
timeout: 3e3,
|
|
7556
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7557
|
+
}).trim();
|
|
7558
|
+
isGitClean = statusOut.length === 0;
|
|
7559
|
+
} catch {
|
|
7560
|
+
}
|
|
7505
7561
|
const candidatePaths = /* @__PURE__ */ new Set();
|
|
7506
7562
|
try {
|
|
7507
|
-
const { execSync:
|
|
7508
|
-
const gitOut =
|
|
7563
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
7564
|
+
const gitOut = execSync7("git status --porcelain", {
|
|
7509
7565
|
cwd: root,
|
|
7510
7566
|
encoding: "utf-8",
|
|
7511
7567
|
timeout: 4e3,
|
|
@@ -7553,9 +7609,10 @@ async function createCheckpoint(label, description) {
|
|
|
7553
7609
|
if (fs22.existsSync(fullPath) && fs22.statSync(fullPath).isFile()) {
|
|
7554
7610
|
const content = fs22.readFileSync(fullPath, "utf-8");
|
|
7555
7611
|
const hash = simpleHash2(content);
|
|
7556
|
-
const
|
|
7612
|
+
const snapFile = path23.join(cpDir, "files", `${fp}.kuma-snap`);
|
|
7613
|
+
const fileDir = path23.dirname(snapFile);
|
|
7557
7614
|
if (!fs22.existsSync(fileDir)) fs22.mkdirSync(fileDir, { recursive: true });
|
|
7558
|
-
fs22.writeFileSync(
|
|
7615
|
+
fs22.writeFileSync(snapFile, content, "utf-8");
|
|
7559
7616
|
files.push({ path: fp, hash });
|
|
7560
7617
|
}
|
|
7561
7618
|
} catch {
|
|
@@ -7567,6 +7624,8 @@ async function createCheckpoint(label, description) {
|
|
|
7567
7624
|
const manifest = {
|
|
7568
7625
|
label,
|
|
7569
7626
|
timestamp: Date.now(),
|
|
7627
|
+
gitHead,
|
|
7628
|
+
isGitClean,
|
|
7570
7629
|
files,
|
|
7571
7630
|
dbSnapshot: true,
|
|
7572
7631
|
description
|
|
@@ -7578,13 +7637,15 @@ async function createCheckpoint(label, description) {
|
|
|
7578
7637
|
);
|
|
7579
7638
|
sessionMemory.recordToolCall("kuma_checkpoint_create", {
|
|
7580
7639
|
label,
|
|
7581
|
-
filesCount: files.length
|
|
7640
|
+
filesCount: files.length,
|
|
7641
|
+
gitHead
|
|
7582
7642
|
});
|
|
7643
|
+
const fileNotice = files.length > 0 ? `\u{1F4E6} **${files.length} file(s)** snapshotted` : gitHead ? `\u{1F4E6} **Git commit snapshotted** (\`${gitHead.substring(0, 8)}\` \u2014 clean tree)` : `\u{1F4E6} **0 file(s)** snapshotted`;
|
|
7583
7644
|
return [
|
|
7584
7645
|
`\u2705 **Checkpoint Created**: "${label}"`,
|
|
7585
7646
|
`\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`,
|
|
7586
7647
|
``,
|
|
7587
|
-
|
|
7648
|
+
fileNotice,
|
|
7588
7649
|
`\u{1F5C4}\uFE0F **Database** snapshotted`,
|
|
7589
7650
|
`\u{1F4CD} ${cpDir}`,
|
|
7590
7651
|
``,
|
|
@@ -7611,10 +7672,12 @@ ${listCheckpoints()}`;
|
|
|
7611
7672
|
let restored = 0;
|
|
7612
7673
|
let failed = 0;
|
|
7613
7674
|
for (const f of manifest.files) {
|
|
7614
|
-
const
|
|
7615
|
-
|
|
7675
|
+
const snapPath = path23.join(cpDir, "files", `${f.path}.kuma-snap`);
|
|
7676
|
+
const legacyPath = path23.join(cpDir, "files", f.path);
|
|
7677
|
+
const sourcePath = fs22.existsSync(snapPath) ? snapPath : legacyPath;
|
|
7678
|
+
if (fs22.existsSync(sourcePath)) {
|
|
7616
7679
|
try {
|
|
7617
|
-
const content = fs22.readFileSync(
|
|
7680
|
+
const content = fs22.readFileSync(sourcePath, "utf-8");
|
|
7618
7681
|
const targetPath = path23.resolve(root, f.path);
|
|
7619
7682
|
const targetDir = path23.dirname(targetPath);
|
|
7620
7683
|
if (!fs22.existsSync(targetDir)) fs22.mkdirSync(targetDir, { recursive: true });
|
|
@@ -7625,6 +7688,20 @@ ${listCheckpoints()}`;
|
|
|
7625
7688
|
}
|
|
7626
7689
|
}
|
|
7627
7690
|
}
|
|
7691
|
+
if (manifest.files.length === 0 && manifest.gitHead) {
|
|
7692
|
+
try {
|
|
7693
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
7694
|
+
execSync7(`git checkout ${manifest.gitHead} -- .`, {
|
|
7695
|
+
cwd: root,
|
|
7696
|
+
encoding: "utf-8",
|
|
7697
|
+
timeout: 8e3,
|
|
7698
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7699
|
+
});
|
|
7700
|
+
restored++;
|
|
7701
|
+
} catch {
|
|
7702
|
+
failed++;
|
|
7703
|
+
}
|
|
7704
|
+
}
|
|
7628
7705
|
const dbSnapshotPath = path23.join(cpDir, "kuma.db");
|
|
7629
7706
|
if (fs22.existsSync(dbSnapshotPath)) {
|
|
7630
7707
|
const kumaDir = path23.join(root, ".kuma");
|
|
@@ -7993,10 +8070,12 @@ async function runAutoVerification(options = {}) {
|
|
|
7993
8070
|
const timeoutMs = options.timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
7994
8071
|
const denial = checkAllowed(root);
|
|
7995
8072
|
if (denial) return denial;
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8073
|
+
if (!options.force) {
|
|
8074
|
+
const runawayBlock = checkRunaway();
|
|
8075
|
+
if (runawayBlock) {
|
|
8076
|
+
releaseFileLock(root);
|
|
8077
|
+
return runawayBlock;
|
|
8078
|
+
}
|
|
8000
8079
|
}
|
|
8001
8080
|
try {
|
|
8002
8081
|
if (!options.force) {
|
|
@@ -8032,8 +8111,8 @@ async function runAutoVerification(options = {}) {
|
|
|
8032
8111
|
let modified = sessionMemory.getModifiedFiles().map((f) => f.filePath);
|
|
8033
8112
|
if (modified.length === 0) {
|
|
8034
8113
|
try {
|
|
8035
|
-
const { execSync:
|
|
8036
|
-
const gitDiff =
|
|
8114
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
8115
|
+
const gitDiff = execSync7("git diff --name-only HEAD", { encoding: "utf-8", timeout: 3e3, cwd: root }).trim();
|
|
8037
8116
|
if (gitDiff) {
|
|
8038
8117
|
modified = gitDiff.split("\n").map((f) => f.trim()).filter(Boolean);
|
|
8039
8118
|
}
|
|
@@ -8173,14 +8252,6 @@ async function runAutoVerification(options = {}) {
|
|
|
8173
8252
|
} catch {
|
|
8174
8253
|
}
|
|
8175
8254
|
}
|
|
8176
|
-
if (passed && !isNoTests && scope !== "session-impact") {
|
|
8177
|
-
try {
|
|
8178
|
-
const { resolveGotchasForScope: resolveGotchasForScope2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
8179
|
-
const { resolved } = await resolveGotchasForScope2(scope);
|
|
8180
|
-
if (resolved > 0) autoGotchaMessage = `\u2705 ${resolved} gotcha(s) resolved automatically (verification passed after file changes).`;
|
|
8181
|
-
} catch {
|
|
8182
|
-
}
|
|
8183
|
-
}
|
|
8184
8255
|
_localRunning = false;
|
|
8185
8256
|
_currentProcess = null;
|
|
8186
8257
|
releaseFileLock(root);
|
|
@@ -8245,7 +8316,7 @@ var init_kumaVerifier = __esm({
|
|
|
8245
8316
|
STALE_RESULT_MS = 3e5;
|
|
8246
8317
|
DEFAULT_TIMEOUT_MS = 6e4;
|
|
8247
8318
|
RUNAWAY_WINDOW_MS = 3e5;
|
|
8248
|
-
RUNAWAY_MAX_CALLS =
|
|
8319
|
+
RUNAWAY_MAX_CALLS = 10;
|
|
8249
8320
|
_verifyCallTimestamps = [];
|
|
8250
8321
|
}
|
|
8251
8322
|
});
|
|
@@ -10519,10 +10590,10 @@ init_sessionMemory();
|
|
|
10519
10590
|
init_kumaDb();
|
|
10520
10591
|
init_kumaGraph();
|
|
10521
10592
|
init_kumaMemory();
|
|
10593
|
+
init_kumaDriftDetector();
|
|
10522
10594
|
init_pathValidator();
|
|
10523
10595
|
import fs16 from "fs";
|
|
10524
10596
|
import path17 from "path";
|
|
10525
|
-
import crypto3 from "crypto";
|
|
10526
10597
|
var CONTEXT_ALIASES = {
|
|
10527
10598
|
"research": "research",
|
|
10528
10599
|
"search": "research",
|
|
@@ -10858,10 +10929,10 @@ async function handleHistory(params) {
|
|
|
10858
10929
|
lines.push(traceFormatted);
|
|
10859
10930
|
} else {
|
|
10860
10931
|
try {
|
|
10861
|
-
const { execSync:
|
|
10932
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
10862
10933
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
10863
10934
|
const root = getProjectRoot2();
|
|
10864
|
-
const gitHistory =
|
|
10935
|
+
const gitHistory = execSync7(
|
|
10865
10936
|
`git log -n 5 --pretty=format:" \u2022 %h (%ad) %s" --date=short -- "${target}"`,
|
|
10866
10937
|
{ cwd: root, encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
10867
10938
|
).trim();
|
|
@@ -10962,24 +11033,6 @@ async function handleImpact(params) {
|
|
|
10962
11033
|
const impact = await analyzeImpact2(target);
|
|
10963
11034
|
return formatImpact2(impact);
|
|
10964
11035
|
}
|
|
10965
|
-
function computeProjectHash(scope) {
|
|
10966
|
-
try {
|
|
10967
|
-
const root = getProjectRoot();
|
|
10968
|
-
const files = fs16.readdirSync(root).slice(0, 20);
|
|
10969
|
-
const hash = crypto3.createHash("md5");
|
|
10970
|
-
hash.update(scope);
|
|
10971
|
-
for (const f of files) {
|
|
10972
|
-
try {
|
|
10973
|
-
const stat = fs16.statSync(path17.join(root, f));
|
|
10974
|
-
hash.update(`${f}:${stat.mtimeMs}`);
|
|
10975
|
-
} catch {
|
|
10976
|
-
}
|
|
10977
|
-
}
|
|
10978
|
-
return hash.digest("hex").substring(0, 12);
|
|
10979
|
-
} catch {
|
|
10980
|
-
return Date.now().toString(16);
|
|
10981
|
-
}
|
|
10982
|
-
}
|
|
10983
11036
|
|
|
10984
11037
|
// src/tools/kumaMemoryTool.ts
|
|
10985
11038
|
init_sessionMemory();
|
|
@@ -11281,7 +11334,7 @@ init_sessionMemory();
|
|
|
11281
11334
|
init_kumaShared();
|
|
11282
11335
|
import fs19 from "fs";
|
|
11283
11336
|
import path20 from "path";
|
|
11284
|
-
import { execSync as
|
|
11337
|
+
import { execSync as execSync5 } from "child_process";
|
|
11285
11338
|
var SCRIPT_PATCH_PATTERNS = [
|
|
11286
11339
|
"writeFileSync",
|
|
11287
11340
|
"writeFile",
|
|
@@ -11375,7 +11428,7 @@ function scanRecentFiles(projectRoot) {
|
|
|
11375
11428
|
function detectGitPatchScripts(projectRoot) {
|
|
11376
11429
|
const warnings = [];
|
|
11377
11430
|
try {
|
|
11378
|
-
const statusStdout =
|
|
11431
|
+
const statusStdout = execSync5("git status --porcelain", {
|
|
11379
11432
|
cwd: projectRoot,
|
|
11380
11433
|
encoding: "utf-8",
|
|
11381
11434
|
timeout: 3e3
|
|
@@ -11408,7 +11461,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
11408
11461
|
}
|
|
11409
11462
|
}
|
|
11410
11463
|
}
|
|
11411
|
-
const deletedStdout =
|
|
11464
|
+
const deletedStdout = execSync5("git diff --name-only --diff-filter=D HEAD", {
|
|
11412
11465
|
cwd: projectRoot,
|
|
11413
11466
|
encoding: "utf-8",
|
|
11414
11467
|
timeout: 3e3
|
|
@@ -11480,7 +11533,7 @@ init_sessionMemory();
|
|
|
11480
11533
|
init_pathValidator();
|
|
11481
11534
|
import fs20 from "fs";
|
|
11482
11535
|
import path21 from "path";
|
|
11483
|
-
import { execSync as
|
|
11536
|
+
import { execSync as execSync6 } from "child_process";
|
|
11484
11537
|
var SNAPSHOT_DIR = "context-snapshots";
|
|
11485
11538
|
function snapshotDir() {
|
|
11486
11539
|
return path21.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
|
|
@@ -11552,7 +11605,7 @@ function formatSnapshot(snapshot) {
|
|
|
11552
11605
|
function getGitDiffStat2() {
|
|
11553
11606
|
try {
|
|
11554
11607
|
const root = getProjectRoot();
|
|
11555
|
-
const stdout =
|
|
11608
|
+
const stdout = execSync6("git diff --stat", {
|
|
11556
11609
|
cwd: root,
|
|
11557
11610
|
encoding: "utf-8",
|
|
11558
11611
|
timeout: 3e3
|
|
@@ -12152,25 +12205,25 @@ async function main() {
|
|
|
12152
12205
|
} catch {
|
|
12153
12206
|
}
|
|
12154
12207
|
try {
|
|
12155
|
-
const { execSync:
|
|
12208
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
12156
12209
|
try {
|
|
12157
|
-
|
|
12210
|
+
execSync7("pkill -f 'jest' 2>/dev/null || true");
|
|
12158
12211
|
} catch {
|
|
12159
12212
|
}
|
|
12160
12213
|
try {
|
|
12161
|
-
|
|
12214
|
+
execSync7("pkill -f 'jest-worker' 2>/dev/null || true");
|
|
12162
12215
|
} catch {
|
|
12163
12216
|
}
|
|
12164
12217
|
try {
|
|
12165
|
-
|
|
12218
|
+
execSync7("pkill -f 'pnpm test' 2>/dev/null || true");
|
|
12166
12219
|
} catch {
|
|
12167
12220
|
}
|
|
12168
12221
|
try {
|
|
12169
|
-
|
|
12222
|
+
execSync7("pkill -f 'npm test' 2>/dev/null || true");
|
|
12170
12223
|
} catch {
|
|
12171
12224
|
}
|
|
12172
12225
|
try {
|
|
12173
|
-
|
|
12226
|
+
execSync7("pkill -f 'yarn test' 2>/dev/null || true");
|
|
12174
12227
|
} catch {
|
|
12175
12228
|
}
|
|
12176
12229
|
console.error("\u2705 Killed orphaned test processes");
|
package/hooks/kuma-agy-hooks.js
CHANGED
|
@@ -5,11 +5,18 @@
|
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
|
|
8
|
+
const GENERIC_FILENAMES = new Set([
|
|
9
|
+
"index.ts", "index.js", "index.tsx", "index.jsx",
|
|
10
|
+
"types.ts", "types.d.ts", "constants.ts", "utils.ts",
|
|
11
|
+
"config.ts", "schema.ts", "styles.css", "main.ts", "main.js"
|
|
12
|
+
]);
|
|
13
|
+
|
|
8
14
|
function findGotchasForFile(targetPath) {
|
|
9
15
|
if (!targetPath) return [];
|
|
10
16
|
const results = [];
|
|
11
17
|
const normalized = targetPath.replace(/\\/g, "/");
|
|
12
18
|
const baseName = path.basename(normalized);
|
|
19
|
+
const isGeneric = GENERIC_FILENAMES.has(baseName.toLowerCase());
|
|
13
20
|
|
|
14
21
|
const gotchasMd = path.resolve(process.cwd(), ".kuma", "KNOWN_GOTCHAS.md");
|
|
15
22
|
if (fs.existsSync(gotchasMd)) {
|
|
@@ -17,11 +24,29 @@ function findGotchasForFile(targetPath) {
|
|
|
17
24
|
const content = fs.readFileSync(gotchasMd, "utf-8");
|
|
18
25
|
const sections = content.split(/(?=^###\s+)/m);
|
|
19
26
|
for (const sec of sections) {
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
// Skip resolved or deprecated gotchas
|
|
28
|
+
if (/[-*]\s*(?:🏷️\s*)?\*\*Status\*\*:\s*(?:resolved|deprecated)/i.test(sec)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const headingLine = sec.split("\n")[0] || "";
|
|
33
|
+
const headingMatch = headingLine.match(/^###\s+\[?(.+?)\]?\s*(?:[—–-]+\s*(.+))?$/);
|
|
34
|
+
if (!headingMatch) continue;
|
|
35
|
+
|
|
36
|
+
const secFilePath = headingMatch[1].trim().replace(/\\/g, "/");
|
|
37
|
+
const secBase = path.basename(secFilePath);
|
|
38
|
+
|
|
39
|
+
// Path matching: exact match, suffix match, or non-generic basename match
|
|
40
|
+
const isMatch =
|
|
41
|
+
normalized.endsWith(secFilePath) ||
|
|
42
|
+
secFilePath.endsWith(normalized) ||
|
|
43
|
+
(!isGeneric && secBase.toLowerCase() === baseName.toLowerCase());
|
|
44
|
+
|
|
45
|
+
if (isMatch) {
|
|
46
|
+
const firstLine = headingLine.replace(/^###\s+/, "").trim();
|
|
22
47
|
const trapMatch = sec.match(/- \S*\s*\*\*Trap\*\*:\s*([^\n]+)/i) || sec.match(/- \*\*Issue\*\*:\s*([^\n]+)/i);
|
|
23
48
|
const ruleMatch = sec.match(/- \S*\s*\*\*Rule(?:\/Fix)?\*\*:\s*([^\n]+)/i) || sec.match(/- \*\*Workaround\*\*:\s*([^\n]+)/i);
|
|
24
|
-
const trap = trapMatch ? trapMatch[1].trim() : firstLine;
|
|
49
|
+
const trap = trapMatch ? trapMatch[1].trim() : (headingMatch[2]?.trim() || firstLine);
|
|
25
50
|
const rule = ruleMatch ? ruleMatch[1].trim() : "";
|
|
26
51
|
results.push({ file: baseName, trap, rule });
|
|
27
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
4
4
|
"description": "Safety-first context & orchestration engine for AI coding agents. MCP server with mandatory research pipeline, knowledge graph, impact analysis, decision memory, and safety guard — works with any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -82,6 +82,6 @@
|
|
|
82
82
|
"studio:dev": "bun run packages/ide/studio/src/index.ts",
|
|
83
83
|
"typecheck": "tsc --noEmit",
|
|
84
84
|
"lint": "echo 'No linting configured'",
|
|
85
|
-
"test": "NODE_OPTIONS=--experimental-vm-modules pnpm exec jest"
|
|
85
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules pnpm exec jest --runInBand"
|
|
86
86
|
}
|
|
87
87
|
}
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
3
|
"name": "kuma",
|
|
4
|
-
"version": "2.4.
|
|
4
|
+
"version": "2.4.8",
|
|
5
5
|
"description": "Safety-first context & orchestration engine for AI coding agents. Shadow memory, gotcha lifecycle, blast radius impact analysis, and test verification.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "plumpslabs",
|