@plumpslabs/kuma 2.4.16 → 2.4.19
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 +319 -56
- package/package.json +1 -1
- package/plugin.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1220,8 +1220,8 @@ async function runDoctor() {
|
|
|
1220
1220
|
} catch {
|
|
1221
1221
|
}
|
|
1222
1222
|
try {
|
|
1223
|
-
const { execSync:
|
|
1224
|
-
const psOutput =
|
|
1223
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
1224
|
+
const psOutput = execSync11(
|
|
1225
1225
|
`ps -eo pid,ppid,command | grep -E "(jest|pnpm test|npm test|yarn test)" | grep -v grep | head -10`,
|
|
1226
1226
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
1227
1227
|
).trim();
|
|
@@ -3366,6 +3366,33 @@ async function scanCodebase(options = {}) {
|
|
|
3366
3366
|
}
|
|
3367
3367
|
}
|
|
3368
3368
|
const db = await getDb();
|
|
3369
|
+
const ensureFileNode = async (relPath) => {
|
|
3370
|
+
const fid = nodeId("file", relPath);
|
|
3371
|
+
try {
|
|
3372
|
+
const chk = db.prepare(`SELECT 1 FROM nodes WHERE id = ? LIMIT 1`);
|
|
3373
|
+
chk.bind([fid]);
|
|
3374
|
+
const exists = chk.step();
|
|
3375
|
+
chk.free();
|
|
3376
|
+
if (!exists) {
|
|
3377
|
+
await upsertNode({ id: fid, type: "file", name: relPath, filePath: relPath, metadata: { backbone: true } });
|
|
3378
|
+
result.nodeCount++;
|
|
3379
|
+
}
|
|
3380
|
+
} catch {
|
|
3381
|
+
}
|
|
3382
|
+
return fid;
|
|
3383
|
+
};
|
|
3384
|
+
const nodeExists = (id) => {
|
|
3385
|
+
try {
|
|
3386
|
+
const chk = db.prepare(`SELECT 1 FROM nodes WHERE id = ? LIMIT 1`);
|
|
3387
|
+
chk.bind([id]);
|
|
3388
|
+
const exists = chk.step();
|
|
3389
|
+
chk.free();
|
|
3390
|
+
return exists;
|
|
3391
|
+
} catch {
|
|
3392
|
+
return false;
|
|
3393
|
+
}
|
|
3394
|
+
};
|
|
3395
|
+
const pendingCalls = [];
|
|
3369
3396
|
for (const parsed of parsedFiles) {
|
|
3370
3397
|
const { filePath, symbols, imports, calledSymbols, isTest } = parsed;
|
|
3371
3398
|
const fileId = nodeId("file", filePath);
|
|
@@ -3421,14 +3448,16 @@ async function scanCodebase(options = {}) {
|
|
|
3421
3448
|
if (sym.extends) {
|
|
3422
3449
|
const parentFile = symbolMap.get(sym.extends) || filePath;
|
|
3423
3450
|
const parentId = `class::${parentFile}::${sym.extends}`;
|
|
3424
|
-
|
|
3425
|
-
|
|
3451
|
+
if (nodeExists(parentId)) {
|
|
3452
|
+
await addEdge({ sourceId: symId, targetId: parentId, type: "extends" });
|
|
3453
|
+
result.edgeCount++;
|
|
3454
|
+
}
|
|
3426
3455
|
}
|
|
3427
3456
|
}
|
|
3428
3457
|
for (const imp of imports) {
|
|
3429
3458
|
const resolved = resolveImportPath(filePath, imp.source, root);
|
|
3430
3459
|
if (resolved) {
|
|
3431
|
-
const targetFileId =
|
|
3460
|
+
const targetFileId = await ensureFileNode(resolved);
|
|
3432
3461
|
await addEdge({
|
|
3433
3462
|
sourceId: fileId,
|
|
3434
3463
|
targetId: targetFileId,
|
|
@@ -3449,17 +3478,17 @@ async function scanCodebase(options = {}) {
|
|
|
3449
3478
|
for (const callee of calledSymbols) {
|
|
3450
3479
|
const calleeFile = symbolMap.get(callee);
|
|
3451
3480
|
if (calleeFile && calleeFile !== filePath) {
|
|
3452
|
-
|
|
3453
|
-
await addEdge({
|
|
3454
|
-
sourceId: fileId,
|
|
3455
|
-
targetId: calleeId,
|
|
3456
|
-
type: "calls"
|
|
3457
|
-
});
|
|
3458
|
-
result.edgeCount++;
|
|
3481
|
+
pendingCalls.push({ fileId, callee, calleeFile, filePath });
|
|
3459
3482
|
}
|
|
3460
3483
|
}
|
|
3461
3484
|
result.filesScanned++;
|
|
3462
3485
|
}
|
|
3486
|
+
for (const p of pendingCalls) {
|
|
3487
|
+
const calleeId = `function::${p.calleeFile}::${p.callee}`;
|
|
3488
|
+
if (!nodeExists(calleeId)) continue;
|
|
3489
|
+
await addEdge({ sourceId: p.fileId, targetId: calleeId, type: "calls" });
|
|
3490
|
+
result.edgeCount++;
|
|
3491
|
+
}
|
|
3463
3492
|
saveDb(db);
|
|
3464
3493
|
return result;
|
|
3465
3494
|
}
|
|
@@ -3957,13 +3986,18 @@ var init_kumaSelfHeal = __esm({
|
|
|
3957
3986
|
// src/engine/kumaDriftDetector.ts
|
|
3958
3987
|
var kumaDriftDetector_exports = {};
|
|
3959
3988
|
__export(kumaDriftDetector_exports, {
|
|
3989
|
+
DRIFT_REPORT_FILE: () => DRIFT_REPORT_FILE,
|
|
3990
|
+
DRIFT_REPORT_MAX_ITEMS: () => DRIFT_REPORT_MAX_ITEMS,
|
|
3960
3991
|
computeProjectHash: () => computeProjectHash,
|
|
3961
3992
|
computeScopeHash: () => computeScopeHash,
|
|
3962
3993
|
detectDrift: () => detectDrift,
|
|
3963
3994
|
flagStaleRecords: () => flagStaleRecords,
|
|
3964
3995
|
formatDriftReport: () => formatDriftReport,
|
|
3965
3996
|
getDriftSummary: () => getDriftSummary,
|
|
3966
|
-
hashFile: () => hashFile
|
|
3997
|
+
hashFile: () => hashFile,
|
|
3998
|
+
readDriftReport: () => readDriftReport,
|
|
3999
|
+
toDriftActionItem: () => toDriftActionItem,
|
|
4000
|
+
writeDriftReport: () => writeDriftReport
|
|
3967
4001
|
});
|
|
3968
4002
|
import fs7 from "fs";
|
|
3969
4003
|
import path9 from "path";
|
|
@@ -4184,13 +4218,51 @@ async function getDriftSummary() {
|
|
|
4184
4218
|
if (staleCount === 0) return "\u2705 All records fresh";
|
|
4185
4219
|
return `\u26A0\uFE0F ${staleCount} stale record(s) detected. Use kuma_memory({ action: 'heal' }) to repair.`;
|
|
4186
4220
|
}
|
|
4187
|
-
|
|
4221
|
+
function toDriftActionItem(r) {
|
|
4222
|
+
const stale = r.severity === "stale";
|
|
4223
|
+
const missing = r.severity === "missing";
|
|
4224
|
+
const action = r.source === "research_cache" ? `re-run kuma_context({ action: 'research', scope: '${r.description}' })` : r.filePath ? `rescan '${r.filePath}' (touch via any context call) or run heal` : `run kuma_memory({ action: 'heal' }) to repair`;
|
|
4225
|
+
return {
|
|
4226
|
+
id: `${r.source}#${r.id}`,
|
|
4227
|
+
kind: r.source,
|
|
4228
|
+
target: r.filePath || r.description,
|
|
4229
|
+
reason: missing ? "source gone (file/record missing)" : `content hash mismatch vs current (age ${r.age})`,
|
|
4230
|
+
action: stale || missing ? action : "monitor"
|
|
4231
|
+
};
|
|
4232
|
+
}
|
|
4233
|
+
async function writeDriftReport(maxItems = DRIFT_REPORT_MAX_ITEMS) {
|
|
4234
|
+
const fp = path9.join(getProjectRoot(), DRIFT_REPORT_FILE);
|
|
4235
|
+
const records = await detectDrift();
|
|
4236
|
+
const actionable = records.filter((r) => r.severity === "stale" || r.severity === "missing");
|
|
4237
|
+
const items = actionable.slice(0, maxItems).map(toDriftActionItem);
|
|
4238
|
+
try {
|
|
4239
|
+
fs7.writeFileSync(
|
|
4240
|
+
fp,
|
|
4241
|
+
JSON.stringify({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), count: actionable.length, items }, null, 2),
|
|
4242
|
+
"utf-8"
|
|
4243
|
+
);
|
|
4244
|
+
} catch {
|
|
4245
|
+
}
|
|
4246
|
+
return { items: items.length, path: fp };
|
|
4247
|
+
}
|
|
4248
|
+
function readDriftReport() {
|
|
4249
|
+
try {
|
|
4250
|
+
const fp = path9.join(getProjectRoot(), DRIFT_REPORT_FILE);
|
|
4251
|
+
if (!fs7.existsSync(fp)) return null;
|
|
4252
|
+
return JSON.parse(fs7.readFileSync(fp, "utf-8"));
|
|
4253
|
+
} catch {
|
|
4254
|
+
return null;
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
var computeProjectHash, DRIFT_REPORT_FILE, DRIFT_REPORT_MAX_ITEMS;
|
|
4188
4258
|
var init_kumaDriftDetector = __esm({
|
|
4189
4259
|
"src/engine/kumaDriftDetector.ts"() {
|
|
4190
4260
|
"use strict";
|
|
4191
4261
|
init_kumaDb();
|
|
4192
4262
|
init_pathValidator();
|
|
4193
4263
|
computeProjectHash = (scope) => computeScopeHash(scope) || Date.now().toString(16);
|
|
4264
|
+
DRIFT_REPORT_FILE = ".kuma/drift.json";
|
|
4265
|
+
DRIFT_REPORT_MAX_ITEMS = 20;
|
|
4194
4266
|
}
|
|
4195
4267
|
});
|
|
4196
4268
|
|
|
@@ -4598,12 +4670,17 @@ __export(mapBackbone_exports, {
|
|
|
4598
4670
|
ensureMapped: () => ensureMapped,
|
|
4599
4671
|
extractImportSources: () => extractImportSources,
|
|
4600
4672
|
formatBackbone: () => formatBackbone,
|
|
4673
|
+
getWorktreeChangedFiles: () => getWorktreeChangedFiles,
|
|
4601
4674
|
isMapEmpty: () => isMapEmpty,
|
|
4602
|
-
|
|
4675
|
+
isWorktreeDirty: () => isWorktreeDirty,
|
|
4676
|
+
readMapMeta: () => readMapMeta,
|
|
4677
|
+
readWorktreeDirty: () => readWorktreeDirty,
|
|
4678
|
+
updateWorktreeDirty: () => updateWorktreeDirty
|
|
4603
4679
|
});
|
|
4604
4680
|
import fs9 from "fs";
|
|
4605
4681
|
import path11 from "path";
|
|
4606
4682
|
import fastGlob3 from "fast-glob";
|
|
4683
|
+
import { execSync as execSync4 } from "child_process";
|
|
4607
4684
|
function extractImportSources(line) {
|
|
4608
4685
|
const out = [];
|
|
4609
4686
|
const t = line.trim();
|
|
@@ -4743,6 +4820,52 @@ async function ensureBackbone() {
|
|
|
4743
4820
|
function formatBackbone(r) {
|
|
4744
4821
|
return `\u{1F5FA}\uFE0F Backbone: ${r.files} file(s)${r.capped ? ` (capped at ${BACKBONE_MAX_FILES})` : ""}, ${r.edges} import edge(s) in ${r.ms}ms (~${r.filesPerSec} files/s) \u2014 approximate tier, symbols fill in on demand.`;
|
|
4745
4822
|
}
|
|
4823
|
+
function getWorktreeChangedFiles(root, cap = WORKTREE_CAP) {
|
|
4824
|
+
const out = [];
|
|
4825
|
+
try {
|
|
4826
|
+
const cwd = root || getProjectRoot();
|
|
4827
|
+
const porcelain = execSync4("git status --porcelain --untracked-files=all", {
|
|
4828
|
+
cwd,
|
|
4829
|
+
encoding: "utf-8",
|
|
4830
|
+
timeout: 5e3,
|
|
4831
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4832
|
+
});
|
|
4833
|
+
if (!porcelain.trim()) return out;
|
|
4834
|
+
for (const rawLine of porcelain.split("\n")) {
|
|
4835
|
+
if (out.length >= cap) break;
|
|
4836
|
+
const line = rawLine.replace(/\s+$/, "");
|
|
4837
|
+
if (!line) continue;
|
|
4838
|
+
const m = line.match(/^(.{2})\s+(.+)$/);
|
|
4839
|
+
if (!m) continue;
|
|
4840
|
+
const code = m[1];
|
|
4841
|
+
let p = m[2].trim();
|
|
4842
|
+
const arrow = p.indexOf(" -> ");
|
|
4843
|
+
if (arrow >= 0) p = p.slice(arrow + 4).trim();
|
|
4844
|
+
p = p.replace(/^"|"$/g, "");
|
|
4845
|
+
if (!p) continue;
|
|
4846
|
+
const ext = p.slice(p.lastIndexOf(".")).toLowerCase();
|
|
4847
|
+
if (!WORKTREE_EXTS.has(ext)) continue;
|
|
4848
|
+
const untracked = code[0] === "?" || code[1] === "?";
|
|
4849
|
+
out.push({ file: p, state: untracked ? "untracked" : "modified" });
|
|
4850
|
+
}
|
|
4851
|
+
} catch {
|
|
4852
|
+
}
|
|
4853
|
+
return out;
|
|
4854
|
+
}
|
|
4855
|
+
function updateWorktreeDirty(files, root) {
|
|
4856
|
+
writeMapMeta({ worktreeDirty: [...new Set(files)].slice(0, WORKTREE_CAP), worktreeDirtyAt: Date.now() }, root);
|
|
4857
|
+
}
|
|
4858
|
+
function readWorktreeDirty(root) {
|
|
4859
|
+
const meta = readMapMeta(root);
|
|
4860
|
+
return Array.isArray(meta.worktreeDirty) ? meta.worktreeDirty.filter((f) => typeof f === "string") : [];
|
|
4861
|
+
}
|
|
4862
|
+
function isWorktreeDirty(target, root) {
|
|
4863
|
+
const norm = target.replace(/\\/g, "/");
|
|
4864
|
+
return readWorktreeDirty(root).some((f) => {
|
|
4865
|
+
const nf = f.replace(/\\/g, "/");
|
|
4866
|
+
return nf === norm || nf.endsWith(`/${norm}`) || norm.endsWith(`/${nf}`);
|
|
4867
|
+
});
|
|
4868
|
+
}
|
|
4746
4869
|
async function ensureMapped(files) {
|
|
4747
4870
|
const targets = [...new Set(files.map((f) => f.trim()).filter(Boolean))].slice(0, 10);
|
|
4748
4871
|
if (targets.length === 0) return { filled: 0 };
|
|
@@ -4773,7 +4896,7 @@ async function ensureMapped(files) {
|
|
|
4773
4896
|
return { filled: 0 };
|
|
4774
4897
|
}
|
|
4775
4898
|
}
|
|
4776
|
-
var BACKBONE_MAX_FILES, MAP_META, IMPORT_RES;
|
|
4899
|
+
var BACKBONE_MAX_FILES, MAP_META, IMPORT_RES, WORKTREE_CAP, WORKTREE_EXTS;
|
|
4777
4900
|
var init_mapBackbone = __esm({
|
|
4778
4901
|
"src/engine/mapBackbone.ts"() {
|
|
4779
4902
|
"use strict";
|
|
@@ -4788,6 +4911,31 @@ var init_mapBackbone = __esm({
|
|
|
4788
4911
|
/(?:from)\s+["']?([a-zA-Z0-9_./-]+)["']?\s+import\s+/g,
|
|
4789
4912
|
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
4790
4913
|
];
|
|
4914
|
+
WORKTREE_CAP = 50;
|
|
4915
|
+
WORKTREE_EXTS = /* @__PURE__ */ new Set([
|
|
4916
|
+
".ts",
|
|
4917
|
+
".tsx",
|
|
4918
|
+
".js",
|
|
4919
|
+
".jsx",
|
|
4920
|
+
".mjs",
|
|
4921
|
+
".cjs",
|
|
4922
|
+
".py",
|
|
4923
|
+
".go",
|
|
4924
|
+
".rs",
|
|
4925
|
+
".java",
|
|
4926
|
+
".rb",
|
|
4927
|
+
".php",
|
|
4928
|
+
".swift",
|
|
4929
|
+
".kt",
|
|
4930
|
+
".cs",
|
|
4931
|
+
".vue",
|
|
4932
|
+
".svelte",
|
|
4933
|
+
".json",
|
|
4934
|
+
".yml",
|
|
4935
|
+
".yaml",
|
|
4936
|
+
".toml",
|
|
4937
|
+
".md"
|
|
4938
|
+
]);
|
|
4791
4939
|
}
|
|
4792
4940
|
});
|
|
4793
4941
|
|
|
@@ -4799,7 +4947,7 @@ __export(symbolReach_exports, {
|
|
|
4799
4947
|
});
|
|
4800
4948
|
import fs10 from "fs";
|
|
4801
4949
|
import path12 from "path";
|
|
4802
|
-
import { execSync as
|
|
4950
|
+
import { execSync as execSync5 } from "child_process";
|
|
4803
4951
|
async function graphReach(symbol, limit) {
|
|
4804
4952
|
const rows = [];
|
|
4805
4953
|
try {
|
|
@@ -4851,7 +4999,7 @@ async function graphReach(symbol, limit) {
|
|
|
4851
4999
|
function ripgrepReach(symbol, root, limit) {
|
|
4852
5000
|
const rows = [];
|
|
4853
5001
|
try {
|
|
4854
|
-
const out =
|
|
5002
|
+
const out = execSync5(
|
|
4855
5003
|
`grep -rn -F --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.py" --include="*.go" --include="*.rs" "${symbol.replace(/"/g, "")}" . 2>/dev/null | grep -v node_modules | grep -v "/dist/" | grep -v "/.git/" | grep -v "/.kuma/" | head -40`,
|
|
4856
5004
|
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
4857
5005
|
).trim();
|
|
@@ -5026,7 +5174,7 @@ __export(impactAnalysis_exports, {
|
|
|
5026
5174
|
});
|
|
5027
5175
|
import fs11 from "fs";
|
|
5028
5176
|
import path13 from "path";
|
|
5029
|
-
import { execSync as
|
|
5177
|
+
import { execSync as execSync6 } from "child_process";
|
|
5030
5178
|
import fastGlob4 from "fast-glob";
|
|
5031
5179
|
function extOf(target) {
|
|
5032
5180
|
const base = path13.basename(target);
|
|
@@ -5047,7 +5195,7 @@ function classifyDiffShape(target, root) {
|
|
|
5047
5195
|
}
|
|
5048
5196
|
try {
|
|
5049
5197
|
const cwd = root || getProjectRoot();
|
|
5050
|
-
const diff =
|
|
5198
|
+
const diff = execSync6(`git diff --unified=0 -- "${target}"`, {
|
|
5051
5199
|
cwd,
|
|
5052
5200
|
encoding: "utf-8",
|
|
5053
5201
|
timeout: 4e3,
|
|
@@ -5313,6 +5461,14 @@ async function calculateBlastRadius(target, options = {}) {
|
|
|
5313
5461
|
const rev = getReverseDependencies(owningPackage.name, wsInfo);
|
|
5314
5462
|
downstreamPackages.push(...rev.map((p) => p.name));
|
|
5315
5463
|
}
|
|
5464
|
+
let worktreeNote = "";
|
|
5465
|
+
try {
|
|
5466
|
+
const { isWorktreeDirty: isWorktreeDirty2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
5467
|
+
if (targetType === "file" && isWorktreeDirty2(target, root)) {
|
|
5468
|
+
worktreeNote = "\u{1F4DD} Uncommitted worktree changes on this target \u2014 map reflects work in progress, not committed state";
|
|
5469
|
+
}
|
|
5470
|
+
} catch {
|
|
5471
|
+
}
|
|
5316
5472
|
if (targetType === "file") {
|
|
5317
5473
|
try {
|
|
5318
5474
|
const { ensureMapped: ensureMapped2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
@@ -5380,6 +5536,7 @@ async function calculateBlastRadius(target, options = {}) {
|
|
|
5380
5536
|
`\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
|
|
5381
5537
|
`\u26A0\uFE0F **Risk Level: ${risk.toUpperCase()}**`,
|
|
5382
5538
|
`\u{1F4D0} Diff shape: \`${diffShape}\` (${shapeDetail})`,
|
|
5539
|
+
...worktreeNote ? [worktreeNote] : [],
|
|
5383
5540
|
owningPackage ? `\u{1F4E6} Owning package: \`${owningPackage.name}\` (${owningPackage.path})` : ""
|
|
5384
5541
|
];
|
|
5385
5542
|
if (riskFlags.length > 0) {
|
|
@@ -5643,6 +5800,7 @@ __export(kumaGraph_exports, {
|
|
|
5643
5800
|
getGraphStats: () => getGraphStats,
|
|
5644
5801
|
nodeId: () => nodeId,
|
|
5645
5802
|
propagateImpact: () => propagateImpact,
|
|
5803
|
+
pruneOrphans: () => pruneOrphans,
|
|
5646
5804
|
queryGraph: () => queryGraph,
|
|
5647
5805
|
recordAffects: () => recordAffects,
|
|
5648
5806
|
recordApiRoute: () => recordApiRoute,
|
|
@@ -5719,6 +5877,48 @@ async function pruneGraphIfNeeded() {
|
|
|
5719
5877
|
return { pruned: false, removedNodes: 0, removedEdges: 0 };
|
|
5720
5878
|
}
|
|
5721
5879
|
}
|
|
5880
|
+
async function pruneOrphans(opts = {}) {
|
|
5881
|
+
const olderThanSec = opts.olderThanSec ?? 3600;
|
|
5882
|
+
const dryRun = opts.dryRun ?? false;
|
|
5883
|
+
let orphanNodes = 0;
|
|
5884
|
+
let danglingEdges = 0;
|
|
5885
|
+
try {
|
|
5886
|
+
const db = await getDb();
|
|
5887
|
+
const cutoff = Math.floor(Date.now() / 1e3) - olderThanSec;
|
|
5888
|
+
const dangling = db.exec(`
|
|
5889
|
+
SELECT COUNT(*) FROM edges e
|
|
5890
|
+
WHERE NOT EXISTS (SELECT 1 FROM nodes n WHERE n.id = e.target_id)
|
|
5891
|
+
OR NOT EXISTS (SELECT 1 FROM nodes n WHERE n.id = e.source_id)
|
|
5892
|
+
`);
|
|
5893
|
+
danglingEdges = Number(dangling[0]?.values?.[0]?.[0]) || 0;
|
|
5894
|
+
if (!dryRun && danglingEdges > 0) {
|
|
5895
|
+
db.exec(`
|
|
5896
|
+
DELETE FROM edges
|
|
5897
|
+
WHERE NOT EXISTS (SELECT 1 FROM nodes n WHERE n.id = edges.target_id)
|
|
5898
|
+
OR NOT EXISTS (SELECT 1 FROM nodes n WHERE n.id = edges.source_id)
|
|
5899
|
+
`);
|
|
5900
|
+
}
|
|
5901
|
+
const orphans = db.exec(`
|
|
5902
|
+
SELECT COUNT(*) FROM nodes n
|
|
5903
|
+
WHERE n.type IN ('file','function','class','method','interface','test','feature_domain','arch_flow')
|
|
5904
|
+
AND n.updated_at < ${cutoff}
|
|
5905
|
+
AND NOT EXISTS (SELECT 1 FROM edges e WHERE e.source_id = n.id OR e.target_id = n.id)
|
|
5906
|
+
`);
|
|
5907
|
+
orphanNodes = Number(orphans[0]?.values?.[0]?.[0]) || 0;
|
|
5908
|
+
if (!dryRun && orphanNodes > 0) {
|
|
5909
|
+
db.exec(`
|
|
5910
|
+
DELETE FROM nodes
|
|
5911
|
+
WHERE type IN ('file','function','class','method','interface','test','feature_domain','arch_flow')
|
|
5912
|
+
AND updated_at < ${cutoff}
|
|
5913
|
+
AND id NOT IN (SELECT source_id FROM edges UNION SELECT target_id FROM edges)
|
|
5914
|
+
`);
|
|
5915
|
+
}
|
|
5916
|
+
if (!dryRun && (orphanNodes > 0 || danglingEdges > 0)) saveDb(db);
|
|
5917
|
+
} catch (err) {
|
|
5918
|
+
console.error(`[KumaGraph] pruneOrphans failed: ${err}`);
|
|
5919
|
+
}
|
|
5920
|
+
return { orphanNodes, danglingEdges };
|
|
5921
|
+
}
|
|
5722
5922
|
async function upsertNode(node) {
|
|
5723
5923
|
try {
|
|
5724
5924
|
const db = await getDb();
|
|
@@ -5833,6 +6033,9 @@ async function recordApiRoute(route, handler) {
|
|
|
5833
6033
|
async function recordDomainFlow(params) {
|
|
5834
6034
|
let nodeCount = 0;
|
|
5835
6035
|
let edgeCount = 0;
|
|
6036
|
+
if (!params.hops || params.hops.length === 0) {
|
|
6037
|
+
return { nodeCount, edgeCount };
|
|
6038
|
+
}
|
|
5836
6039
|
try {
|
|
5837
6040
|
const domainId = nodeId("feature_domain", params.domain);
|
|
5838
6041
|
let fileHashes = {};
|
|
@@ -6712,9 +6915,9 @@ ${summary}`;
|
|
|
6712
6915
|
}
|
|
6713
6916
|
async function codebaseImpactFallback(target) {
|
|
6714
6917
|
try {
|
|
6715
|
-
const { execSync:
|
|
6918
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
6716
6919
|
const root = process.cwd();
|
|
6717
|
-
const grepResult =
|
|
6920
|
+
const grepResult = execSync11(
|
|
6718
6921
|
`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`,
|
|
6719
6922
|
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
6720
6923
|
).trim();
|
|
@@ -6860,7 +7063,7 @@ __export(kumaShared_exports, {
|
|
|
6860
7063
|
isReadTool: () => isReadTool,
|
|
6861
7064
|
isTestTool: () => isTestTool
|
|
6862
7065
|
});
|
|
6863
|
-
import { execSync as
|
|
7066
|
+
import { execSync as execSync7 } from "child_process";
|
|
6864
7067
|
import fs12 from "fs";
|
|
6865
7068
|
import path14 from "path";
|
|
6866
7069
|
function isEditTool(toolName) {
|
|
@@ -6900,7 +7103,7 @@ function getSessionStats(inputGoal) {
|
|
|
6900
7103
|
function getGitDiffStat(timeout = 3e3) {
|
|
6901
7104
|
try {
|
|
6902
7105
|
const root = getProjectRoot();
|
|
6903
|
-
return
|
|
7106
|
+
return execSync7("git diff --stat", {
|
|
6904
7107
|
cwd: root,
|
|
6905
7108
|
encoding: "utf-8",
|
|
6906
7109
|
timeout
|
|
@@ -6913,13 +7116,16 @@ function getUncommittedFiles(timeout = 3e3) {
|
|
|
6913
7116
|
const files = /* @__PURE__ */ new Set();
|
|
6914
7117
|
try {
|
|
6915
7118
|
const root = getProjectRoot();
|
|
6916
|
-
const porcelain =
|
|
7119
|
+
const porcelain = execSync7("git status --porcelain", {
|
|
6917
7120
|
cwd: root,
|
|
6918
7121
|
encoding: "utf-8",
|
|
6919
7122
|
timeout,
|
|
6920
7123
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6921
|
-
})
|
|
6922
|
-
|
|
7124
|
+
});
|
|
7125
|
+
if (!porcelain.trim()) return files;
|
|
7126
|
+
for (const rawLine of porcelain.split("\n")) {
|
|
7127
|
+
const line = rawLine.replace(/\s+$/, "");
|
|
7128
|
+
if (!line) continue;
|
|
6923
7129
|
const m = line.match(/^.{3}(.+?)(?:\s+->\s+.+)?$/);
|
|
6924
7130
|
if (m && m[1]) files.add(m[1].trim().replace(/^"|"$/g, ""));
|
|
6925
7131
|
}
|
|
@@ -6932,17 +7138,17 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6932
7138
|
let base = "";
|
|
6933
7139
|
try {
|
|
6934
7140
|
const root = getProjectRoot();
|
|
6935
|
-
const branch =
|
|
7141
|
+
const branch = execSync7("git rev-parse --abbrev-ref HEAD", {
|
|
6936
7142
|
cwd: root,
|
|
6937
7143
|
encoding: "utf-8",
|
|
6938
7144
|
timeout,
|
|
6939
7145
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6940
7146
|
}).trim();
|
|
6941
|
-
const baseRef =
|
|
7147
|
+
const baseRef = execSync7(
|
|
6942
7148
|
`git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo origin/${branch === "main" || branch === "master" ? "HEAD" : "main"}`,
|
|
6943
7149
|
{ cwd: root, encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }
|
|
6944
7150
|
).trim().replace("refs/remotes/", "");
|
|
6945
|
-
const mergeBase =
|
|
7151
|
+
const mergeBase = execSync7(`git merge-base HEAD ${baseRef} 2>/dev/null || git rev-list --max-parents=0 HEAD`, {
|
|
6946
7152
|
cwd: root,
|
|
6947
7153
|
encoding: "utf-8",
|
|
6948
7154
|
timeout,
|
|
@@ -6950,7 +7156,7 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6950
7156
|
}).trim().split("\n")[0];
|
|
6951
7157
|
base = baseRef;
|
|
6952
7158
|
if (mergeBase) {
|
|
6953
|
-
const out =
|
|
7159
|
+
const out = execSync7(`git diff --name-only ${mergeBase} HEAD`, {
|
|
6954
7160
|
cwd: root,
|
|
6955
7161
|
encoding: "utf-8",
|
|
6956
7162
|
timeout,
|
|
@@ -7074,7 +7280,7 @@ function detectTestWeakening(files, root) {
|
|
|
7074
7280
|
for (const f of files) {
|
|
7075
7281
|
if (!TEST_FILE_PATTERN.test(f)) continue;
|
|
7076
7282
|
try {
|
|
7077
|
-
const diff =
|
|
7283
|
+
const diff = execSync7(`git diff --unified=0 -- "${f}"`, {
|
|
7078
7284
|
cwd,
|
|
7079
7285
|
encoding: "utf-8",
|
|
7080
7286
|
timeout: 4e3,
|
|
@@ -10307,7 +10513,7 @@ __export(docDrift_exports, {
|
|
|
10307
10513
|
});
|
|
10308
10514
|
import fs29 from "fs";
|
|
10309
10515
|
import path32 from "path";
|
|
10310
|
-
import { execSync as
|
|
10516
|
+
import { execSync as execSync10 } from "child_process";
|
|
10311
10517
|
function collectDocFiles(root) {
|
|
10312
10518
|
const cwd = root || getProjectRoot();
|
|
10313
10519
|
const out = [];
|
|
@@ -10366,7 +10572,7 @@ function codeContainsBatch(root, needles) {
|
|
|
10366
10572
|
const chunk = clean.slice(i, i + 50);
|
|
10367
10573
|
try {
|
|
10368
10574
|
const args = chunk.map((n) => `-e "${n}"`).join(" ");
|
|
10369
|
-
const out =
|
|
10575
|
+
const out = execSync10(
|
|
10370
10576
|
`grep -rn --include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" --include="*.py" --include="*.go" --include="*.rs" --include="*.json" --include="*.yml" --include="*.yaml" --include="*.toml" --include=".env*" -F ${args} . 2>/dev/null | grep -v node_modules | grep -v "/dist/" | grep -v "/.git/" | grep -v "/.kuma/" | head -100`,
|
|
10371
10577
|
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
10372
10578
|
).trim();
|
|
@@ -10570,14 +10776,14 @@ async function createCheckpoint(label, description) {
|
|
|
10570
10776
|
let gitHead;
|
|
10571
10777
|
let isGitClean = false;
|
|
10572
10778
|
try {
|
|
10573
|
-
const { execSync:
|
|
10574
|
-
gitHead =
|
|
10779
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
10780
|
+
gitHead = execSync11("git rev-parse HEAD", {
|
|
10575
10781
|
cwd: root,
|
|
10576
10782
|
encoding: "utf-8",
|
|
10577
10783
|
timeout: 3e3,
|
|
10578
10784
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10579
10785
|
}).trim();
|
|
10580
|
-
const statusOut =
|
|
10786
|
+
const statusOut = execSync11("git status --porcelain", {
|
|
10581
10787
|
cwd: root,
|
|
10582
10788
|
encoding: "utf-8",
|
|
10583
10789
|
timeout: 3e3,
|
|
@@ -10588,8 +10794,8 @@ async function createCheckpoint(label, description) {
|
|
|
10588
10794
|
}
|
|
10589
10795
|
const candidatePaths = /* @__PURE__ */ new Set();
|
|
10590
10796
|
try {
|
|
10591
|
-
const { execSync:
|
|
10592
|
-
const gitOut =
|
|
10797
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
10798
|
+
const gitOut = execSync11("git status --porcelain", {
|
|
10593
10799
|
cwd: root,
|
|
10594
10800
|
encoding: "utf-8",
|
|
10595
10801
|
timeout: 4e3,
|
|
@@ -10718,8 +10924,8 @@ ${listCheckpoints()}`;
|
|
|
10718
10924
|
}
|
|
10719
10925
|
if (manifest.files.length === 0 && manifest.gitHead) {
|
|
10720
10926
|
try {
|
|
10721
|
-
const { execSync:
|
|
10722
|
-
|
|
10927
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
10928
|
+
execSync11(`git checkout ${manifest.gitHead} -- .`, {
|
|
10723
10929
|
cwd: root,
|
|
10724
10930
|
encoding: "utf-8",
|
|
10725
10931
|
timeout: 8e3,
|
|
@@ -11266,6 +11472,29 @@ function opencodePluginTemplate() {
|
|
|
11266
11472
|
" },",
|
|
11267
11473
|
" };",
|
|
11268
11474
|
"};",
|
|
11475
|
+
"",
|
|
11476
|
+
"// Server-runtime definition (required by newer OpenCode server).",
|
|
11477
|
+
"// Reuses the same hooks via the named export above.",
|
|
11478
|
+
"const kumaHooks = {",
|
|
11479
|
+
' "tool.execute.before": async (input, output) => {',
|
|
11480
|
+
" const hooks = await KumaPlugin();",
|
|
11481
|
+
' return hooks["tool.execute.before"](input, output);',
|
|
11482
|
+
" },",
|
|
11483
|
+
' "file.edited": async (input) => {',
|
|
11484
|
+
" const hooks = await KumaPlugin();",
|
|
11485
|
+
' return hooks["file.edited"](input);',
|
|
11486
|
+
" },",
|
|
11487
|
+
' "experimental.session.compacting": async (input, output) => {',
|
|
11488
|
+
" const hooks = await KumaPlugin();",
|
|
11489
|
+
' return hooks["experimental.session.compacting"](input, output);',
|
|
11490
|
+
" },",
|
|
11491
|
+
"};",
|
|
11492
|
+
"",
|
|
11493
|
+
"export default {",
|
|
11494
|
+
' id: "kuma",',
|
|
11495
|
+
" setup: async () => kumaHooks,",
|
|
11496
|
+
" effect: async () => kumaHooks,",
|
|
11497
|
+
"};",
|
|
11269
11498
|
""
|
|
11270
11499
|
].join("\n");
|
|
11271
11500
|
}
|
|
@@ -12769,6 +12998,20 @@ async function daemonTick(root) {
|
|
|
12769
12998
|
} catch (err) {
|
|
12770
12999
|
notes.push(`map: skipped (${err})`);
|
|
12771
13000
|
}
|
|
13001
|
+
try {
|
|
13002
|
+
const { getWorktreeChangedFiles: getWorktreeChangedFiles2, updateWorktreeDirty: updateWorktreeDirty2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
13003
|
+
const { scanCodebase: scanCodebase2 } = await Promise.resolve().then(() => (init_kumaCodeScanner(), kumaCodeScanner_exports));
|
|
13004
|
+
const dirty = getWorktreeChangedFiles2(r, 50);
|
|
13005
|
+
if (dirty.length > 0) {
|
|
13006
|
+
const res = await scanCodebase2({ include: dirty.map((d) => d.file), force: true, maxFiles: dirty.length });
|
|
13007
|
+
updateWorktreeDirty2(dirty.map((d) => d.file), r);
|
|
13008
|
+
notes.push(`worktree: ${dirty.length} dirty forced (${res.filesScanned} scanned)`);
|
|
13009
|
+
} else {
|
|
13010
|
+
updateWorktreeDirty2([], r);
|
|
13011
|
+
}
|
|
13012
|
+
} catch (err) {
|
|
13013
|
+
notes.push(`worktree: skipped (${err})`);
|
|
13014
|
+
}
|
|
12772
13015
|
try {
|
|
12773
13016
|
const { refreshPackageBriefs: refreshPackageBriefs2 } = await Promise.resolve().then(() => (init_packageBriefs(), packageBriefs_exports));
|
|
12774
13017
|
const { count } = await refreshPackageBriefs2();
|
|
@@ -12784,6 +13027,20 @@ async function daemonTick(root) {
|
|
|
12784
13027
|
} catch {
|
|
12785
13028
|
notes.push("drift: skipped");
|
|
12786
13029
|
}
|
|
13030
|
+
try {
|
|
13031
|
+
const { writeDriftReport: writeDriftReport2 } = await Promise.resolve().then(() => (init_kumaDriftDetector(), kumaDriftDetector_exports));
|
|
13032
|
+
const { items } = await writeDriftReport2();
|
|
13033
|
+
notes.push(`drift-report: ${items} item(s) \u2192 .kuma/drift.json`);
|
|
13034
|
+
} catch {
|
|
13035
|
+
}
|
|
13036
|
+
try {
|
|
13037
|
+
const { pruneOrphans: pruneOrphans2 } = await Promise.resolve().then(() => (init_kumaGraph(), kumaGraph_exports));
|
|
13038
|
+
const { orphanNodes, danglingEdges } = await pruneOrphans2({ olderThanSec: 3600 });
|
|
13039
|
+
if (orphanNodes > 0 || danglingEdges > 0) {
|
|
13040
|
+
notes.push(`orphans: -${orphanNodes}n/-${danglingEdges}e pruned`);
|
|
13041
|
+
}
|
|
13042
|
+
} catch {
|
|
13043
|
+
}
|
|
12787
13044
|
try {
|
|
12788
13045
|
const { clearDirty: clearDirty2 } = await Promise.resolve().then(() => (init_cacheFreshness(), cacheFreshness_exports));
|
|
12789
13046
|
clearDirty2();
|
|
@@ -14023,10 +14280,10 @@ async function handleHistory(params) {
|
|
|
14023
14280
|
lines.push(traceFormatted);
|
|
14024
14281
|
} else {
|
|
14025
14282
|
try {
|
|
14026
|
-
const { execSync:
|
|
14283
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
14027
14284
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
14028
14285
|
const root = getProjectRoot2();
|
|
14029
|
-
const gitHistory =
|
|
14286
|
+
const gitHistory = execSync11(
|
|
14030
14287
|
`git log -n 5 --pretty=format:" \u2022 %h (%ad) %s" --date=short -- "${target}"`,
|
|
14031
14288
|
{ cwd: root, encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
14032
14289
|
).trim();
|
|
@@ -14457,7 +14714,7 @@ init_sessionMemory();
|
|
|
14457
14714
|
init_kumaShared();
|
|
14458
14715
|
import fs26 from "fs";
|
|
14459
14716
|
import path29 from "path";
|
|
14460
|
-
import { execSync as
|
|
14717
|
+
import { execSync as execSync8 } from "child_process";
|
|
14461
14718
|
var SCRIPT_PATCH_PATTERNS = [
|
|
14462
14719
|
"writeFileSync",
|
|
14463
14720
|
"writeFile",
|
|
@@ -14551,7 +14808,7 @@ function scanRecentFiles(projectRoot) {
|
|
|
14551
14808
|
function detectGitPatchScripts(projectRoot) {
|
|
14552
14809
|
const warnings = [];
|
|
14553
14810
|
try {
|
|
14554
|
-
const statusStdout =
|
|
14811
|
+
const statusStdout = execSync8("git status --porcelain", {
|
|
14555
14812
|
cwd: projectRoot,
|
|
14556
14813
|
encoding: "utf-8",
|
|
14557
14814
|
timeout: 3e3
|
|
@@ -14584,7 +14841,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
14584
14841
|
}
|
|
14585
14842
|
}
|
|
14586
14843
|
}
|
|
14587
|
-
const deletedStdout =
|
|
14844
|
+
const deletedStdout = execSync8("git diff --name-only --diff-filter=D HEAD", {
|
|
14588
14845
|
cwd: projectRoot,
|
|
14589
14846
|
encoding: "utf-8",
|
|
14590
14847
|
timeout: 3e3
|
|
@@ -14656,7 +14913,7 @@ init_sessionMemory();
|
|
|
14656
14913
|
init_pathValidator();
|
|
14657
14914
|
import fs27 from "fs";
|
|
14658
14915
|
import path30 from "path";
|
|
14659
|
-
import { execSync as
|
|
14916
|
+
import { execSync as execSync9 } from "child_process";
|
|
14660
14917
|
var SNAPSHOT_DIR = "context-snapshots";
|
|
14661
14918
|
function snapshotDir() {
|
|
14662
14919
|
return path30.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
|
|
@@ -14728,7 +14985,7 @@ function formatSnapshot(snapshot) {
|
|
|
14728
14985
|
function getGitDiffStat2() {
|
|
14729
14986
|
try {
|
|
14730
14987
|
const root = getProjectRoot();
|
|
14731
|
-
const stdout =
|
|
14988
|
+
const stdout = execSync9("git diff --stat", {
|
|
14732
14989
|
cwd: root,
|
|
14733
14990
|
encoding: "utf-8",
|
|
14734
14991
|
timeout: 3e3
|
|
@@ -15513,25 +15770,25 @@ async function main() {
|
|
|
15513
15770
|
} catch {
|
|
15514
15771
|
}
|
|
15515
15772
|
try {
|
|
15516
|
-
const { execSync:
|
|
15773
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
15517
15774
|
try {
|
|
15518
|
-
|
|
15775
|
+
execSync11("pkill -f 'jest' 2>/dev/null || true");
|
|
15519
15776
|
} catch {
|
|
15520
15777
|
}
|
|
15521
15778
|
try {
|
|
15522
|
-
|
|
15779
|
+
execSync11("pkill -f 'jest-worker' 2>/dev/null || true");
|
|
15523
15780
|
} catch {
|
|
15524
15781
|
}
|
|
15525
15782
|
try {
|
|
15526
|
-
|
|
15783
|
+
execSync11("pkill -f 'pnpm test' 2>/dev/null || true");
|
|
15527
15784
|
} catch {
|
|
15528
15785
|
}
|
|
15529
15786
|
try {
|
|
15530
|
-
|
|
15787
|
+
execSync11("pkill -f 'npm test' 2>/dev/null || true");
|
|
15531
15788
|
} catch {
|
|
15532
15789
|
}
|
|
15533
15790
|
try {
|
|
15534
|
-
|
|
15791
|
+
execSync11("pkill -f 'yarn test' 2>/dev/null || true");
|
|
15535
15792
|
} catch {
|
|
15536
15793
|
}
|
|
15537
15794
|
console.error("\u2705 Killed orphaned test processes");
|
|
@@ -15680,6 +15937,12 @@ ${context}` : "";
|
|
|
15680
15937
|
await refreshPackageBriefs2();
|
|
15681
15938
|
} catch {
|
|
15682
15939
|
}
|
|
15940
|
+
try {
|
|
15941
|
+
const { getWorktreeChangedFiles: getWorktreeChangedFiles2, updateWorktreeDirty: updateWorktreeDirty2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
15942
|
+
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
15943
|
+
updateWorktreeDirty2(getWorktreeChangedFiles2(getProjectRoot2(), 50).map((d) => d.file), getProjectRoot2());
|
|
15944
|
+
} catch {
|
|
15945
|
+
}
|
|
15683
15946
|
try {
|
|
15684
15947
|
const { clearDirty: clearDirty2 } = await Promise.resolve().then(() => (init_cacheFreshness(), cacheFreshness_exports));
|
|
15685
15948
|
clearDirty2();
|
|
@@ -15757,8 +16020,8 @@ ${line}
|
|
|
15757
16020
|
} catch {
|
|
15758
16021
|
}
|
|
15759
16022
|
try {
|
|
15760
|
-
const { execSync:
|
|
15761
|
-
const branch =
|
|
16023
|
+
const { execSync: execSync11 } = await import("child_process");
|
|
16024
|
+
const branch = execSync11("git rev-parse --abbrev-ref HEAD", {
|
|
15762
16025
|
cwd: root,
|
|
15763
16026
|
encoding: "utf-8",
|
|
15764
16027
|
timeout: 2e3,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.19",
|
|
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",
|
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.19",
|
|
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",
|