@plumpslabs/kuma 2.4.14 → 2.4.15
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 +868 -519
- 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: execSync10 } = await import("child_process");
|
|
1224
|
+
const psOutput = execSync10(
|
|
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();
|
|
@@ -3251,6 +3251,18 @@ function parseFallbackRegex(filePath, content) {
|
|
|
3251
3251
|
if (impMatch && (impMatch[1].startsWith(".") || impMatch[1].startsWith("/"))) {
|
|
3252
3252
|
result.imports.push({ source: impMatch[1], symbols: [], isDefault: false });
|
|
3253
3253
|
}
|
|
3254
|
+
if (!fnMatch && !clsMatch) {
|
|
3255
|
+
const callRe = /([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g;
|
|
3256
|
+
let m;
|
|
3257
|
+
let count = 0;
|
|
3258
|
+
while ((m = callRe.exec(line)) !== null && count < 5) {
|
|
3259
|
+
const name = m[1];
|
|
3260
|
+
if (!isJsBuiltin(name) && !result.calledSymbols.includes(name)) {
|
|
3261
|
+
result.calledSymbols.push(name);
|
|
3262
|
+
count++;
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3254
3266
|
}
|
|
3255
3267
|
return result;
|
|
3256
3268
|
}
|
|
@@ -4577,6 +4589,124 @@ var init_workspaceIntelligence = __esm({
|
|
|
4577
4589
|
}
|
|
4578
4590
|
});
|
|
4579
4591
|
|
|
4592
|
+
// src/engine/symbolReach.ts
|
|
4593
|
+
var symbolReach_exports = {};
|
|
4594
|
+
__export(symbolReach_exports, {
|
|
4595
|
+
formatReachability: () => formatReachability,
|
|
4596
|
+
symbolReachability: () => symbolReachability
|
|
4597
|
+
});
|
|
4598
|
+
import fs9 from "fs";
|
|
4599
|
+
import path11 from "path";
|
|
4600
|
+
import { execSync as execSync4 } from "child_process";
|
|
4601
|
+
async function graphReach(symbol, limit) {
|
|
4602
|
+
const rows = [];
|
|
4603
|
+
try {
|
|
4604
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
4605
|
+
const db = await getDb2();
|
|
4606
|
+
const symStmt = db.prepare(
|
|
4607
|
+
`SELECT id, file_path FROM nodes WHERE type IN ('function','class','method') AND (name = ? OR name LIKE ?) LIMIT 5`
|
|
4608
|
+
);
|
|
4609
|
+
symStmt.bind([symbol, `%::${symbol}`]);
|
|
4610
|
+
const symIds = [];
|
|
4611
|
+
while (symStmt.step()) {
|
|
4612
|
+
const r = symStmt.getAsObject();
|
|
4613
|
+
symIds.push({ id: r.id, file: r.file_path });
|
|
4614
|
+
}
|
|
4615
|
+
symStmt.free();
|
|
4616
|
+
if (symIds.length === 0) return [];
|
|
4617
|
+
for (const s of symIds) {
|
|
4618
|
+
const cStmt = db.prepare(
|
|
4619
|
+
`SELECT DISTINCT n_src.file_path as fp FROM edges e JOIN nodes n_src ON n_src.id = e.source_id WHERE e.target_id = ? AND e.type = 'calls' LIMIT 20`
|
|
4620
|
+
);
|
|
4621
|
+
cStmt.bind([s.id]);
|
|
4622
|
+
const callers = /* @__PURE__ */ new Set();
|
|
4623
|
+
while (cStmt.step()) {
|
|
4624
|
+
const fp = cStmt.getAsObject().fp;
|
|
4625
|
+
if (fp) callers.add(fp);
|
|
4626
|
+
}
|
|
4627
|
+
cStmt.free();
|
|
4628
|
+
for (const fp of [...callers, s.file]) {
|
|
4629
|
+
const tStmt = db.prepare(
|
|
4630
|
+
`SELECT DISTINCT n.file_path as tfp FROM edges e JOIN nodes n ON n.id = e.source_id WHERE e.type = 'tests' AND (e.target_id LIKE ? OR e.target_id LIKE ?) LIMIT 5`
|
|
4631
|
+
);
|
|
4632
|
+
tStmt.bind([`%${fp}%`, `%${s.id}%`]);
|
|
4633
|
+
while (tStmt.step()) {
|
|
4634
|
+
const tfp = tStmt.getAsObject().tfp;
|
|
4635
|
+
if (tfp && !rows.some((r) => r.test === tfp)) {
|
|
4636
|
+
rows.push({ test: tfp, via: fp, evidence: `${tfp} tests ${fp} (graph edge)` });
|
|
4637
|
+
}
|
|
4638
|
+
if (rows.length >= limit) break;
|
|
4639
|
+
}
|
|
4640
|
+
tStmt.free();
|
|
4641
|
+
if (rows.length >= limit) break;
|
|
4642
|
+
}
|
|
4643
|
+
if (rows.length >= limit) break;
|
|
4644
|
+
}
|
|
4645
|
+
} catch {
|
|
4646
|
+
}
|
|
4647
|
+
return rows;
|
|
4648
|
+
}
|
|
4649
|
+
function ripgrepReach(symbol, root, limit) {
|
|
4650
|
+
const rows = [];
|
|
4651
|
+
try {
|
|
4652
|
+
const out = execSync4(
|
|
4653
|
+
`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`,
|
|
4654
|
+
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
4655
|
+
).trim();
|
|
4656
|
+
if (!out) return [];
|
|
4657
|
+
const callers = /* @__PURE__ */ new Set();
|
|
4658
|
+
for (const line of out.split("\n")) {
|
|
4659
|
+
const m = line.match(/^([^:]+):(\d+):(.*)$/);
|
|
4660
|
+
if (!m) continue;
|
|
4661
|
+
const [, fp, ln, code] = m;
|
|
4662
|
+
if (new RegExp(`\\b${symbol}\\s*\\(`).test(code)) callers.add(`${fp}:${ln}`);
|
|
4663
|
+
}
|
|
4664
|
+
for (const c of [...callers].slice(0, BERBEDA)) {
|
|
4665
|
+
const [fp] = c.split(":");
|
|
4666
|
+
const dir = path11.dirname(fp);
|
|
4667
|
+
const stem = path11.basename(fp, path11.extname(fp)).split(/[-_.]/)[0];
|
|
4668
|
+
try {
|
|
4669
|
+
const dirFull = path11.join(root, dir);
|
|
4670
|
+
if (!fs9.existsSync(dirFull)) continue;
|
|
4671
|
+
for (const f of fs9.readdirSync(dirFull)) {
|
|
4672
|
+
if (!/(\.test\.|\.spec\.|_test\.)/i.test(f)) continue;
|
|
4673
|
+
const low = f.toLowerCase();
|
|
4674
|
+
if (low.includes(stem.toLowerCase()) || low.includes(symbol.toLowerCase())) {
|
|
4675
|
+
const rel = path11.join(dir, f);
|
|
4676
|
+
if (!rows.some((r) => r.test === rel)) {
|
|
4677
|
+
rows.push({ test: rel, via: fp, evidence: `${rel} sibling-matches caller ${fp} (called at ${c})` });
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
if (rows.length >= limit) break;
|
|
4681
|
+
}
|
|
4682
|
+
} catch {
|
|
4683
|
+
}
|
|
4684
|
+
if (rows.length >= limit) break;
|
|
4685
|
+
}
|
|
4686
|
+
} catch {
|
|
4687
|
+
}
|
|
4688
|
+
return rows;
|
|
4689
|
+
}
|
|
4690
|
+
async function symbolReachability(symbol, limit = 10, root) {
|
|
4691
|
+
const g = await graphReach(symbol, limit);
|
|
4692
|
+
if (g.length > 0) return { tier: 1, rows: g };
|
|
4693
|
+
return { tier: 2, rows: ripgrepReach(symbol, root || getProjectRoot(), limit) };
|
|
4694
|
+
}
|
|
4695
|
+
function formatReachability(symbol, tier, rows) {
|
|
4696
|
+
if (rows.length === 0) return `\u{1F50E} Reachability \`${symbol}\` (tier ${tier}): no tests reach this symbol.`;
|
|
4697
|
+
const lines = [`\u{1F50E} Reachability \`${symbol}\` (tier ${tier}, ${rows.length} test(s)):`];
|
|
4698
|
+
for (const r of rows.slice(0, 5)) lines.push(` \u2022 \`${r.test}\` via \`${r.via}\` \u2014 ${r.evidence}`);
|
|
4699
|
+
return lines.join("\n");
|
|
4700
|
+
}
|
|
4701
|
+
var BERBEDA;
|
|
4702
|
+
var init_symbolReach = __esm({
|
|
4703
|
+
"src/engine/symbolReach.ts"() {
|
|
4704
|
+
"use strict";
|
|
4705
|
+
init_pathValidator();
|
|
4706
|
+
BERBEDA = 10;
|
|
4707
|
+
}
|
|
4708
|
+
});
|
|
4709
|
+
|
|
4580
4710
|
// src/engine/testHistory.ts
|
|
4581
4711
|
var testHistory_exports = {};
|
|
4582
4712
|
__export(testHistory_exports, {
|
|
@@ -4692,17 +4822,17 @@ __export(impactAnalysis_exports, {
|
|
|
4692
4822
|
classifyDiffShape: () => classifyDiffShape,
|
|
4693
4823
|
getIndexerStatus: () => getIndexerStatus
|
|
4694
4824
|
});
|
|
4695
|
-
import
|
|
4696
|
-
import
|
|
4697
|
-
import { execSync as
|
|
4825
|
+
import fs10 from "fs";
|
|
4826
|
+
import path12 from "path";
|
|
4827
|
+
import { execSync as execSync5 } from "child_process";
|
|
4698
4828
|
import fastGlob3 from "fast-glob";
|
|
4699
4829
|
function extOf(target) {
|
|
4700
|
-
const base =
|
|
4830
|
+
const base = path12.basename(target);
|
|
4701
4831
|
const dot = base.lastIndexOf(".");
|
|
4702
4832
|
return dot > 0 ? base.slice(dot).toLowerCase() : "";
|
|
4703
4833
|
}
|
|
4704
4834
|
function isExampleOrDocName(target) {
|
|
4705
|
-
const base =
|
|
4835
|
+
const base = path12.basename(target).toLowerCase();
|
|
4706
4836
|
return base.endsWith(".example") || base.startsWith(".env") || base.endsWith(".md") || base.endsWith(".txt") || base.endsWith(".rst") || base === "license" || base.startsWith("license.");
|
|
4707
4837
|
}
|
|
4708
4838
|
function classifyDiffShape(target, root) {
|
|
@@ -4715,7 +4845,7 @@ function classifyDiffShape(target, root) {
|
|
|
4715
4845
|
}
|
|
4716
4846
|
try {
|
|
4717
4847
|
const cwd = root || getProjectRoot();
|
|
4718
|
-
const diff =
|
|
4848
|
+
const diff = execSync5(`git diff --unified=0 -- "${target}"`, {
|
|
4719
4849
|
cwd,
|
|
4720
4850
|
encoding: "utf-8",
|
|
4721
4851
|
timeout: 4e3,
|
|
@@ -4735,7 +4865,7 @@ function classifyDiffShape(target, root) {
|
|
|
4735
4865
|
return { shape: "runtime-code", detail: "runtime code change" };
|
|
4736
4866
|
}
|
|
4737
4867
|
async function findImportingFiles(targetFile, root) {
|
|
4738
|
-
const normTarget = normalizeScope(
|
|
4868
|
+
const normTarget = normalizeScope(path12.normalize(targetFile)) || path12.normalize(targetFile);
|
|
4739
4869
|
try {
|
|
4740
4870
|
const db = await getDb();
|
|
4741
4871
|
const targetFileId = `file::${normTarget}`;
|
|
@@ -4774,8 +4904,8 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4774
4904
|
}
|
|
4775
4905
|
} catch {
|
|
4776
4906
|
}
|
|
4777
|
-
const baseName =
|
|
4778
|
-
const relTarget =
|
|
4907
|
+
const baseName = path12.basename(normTarget, path12.extname(normTarget));
|
|
4908
|
+
const relTarget = path12.isAbsolute(normTarget) ? path12.relative(root, normTarget) : normTarget;
|
|
4779
4909
|
try {
|
|
4780
4910
|
const files = await fastGlob3(
|
|
4781
4911
|
["**/*.{ts,tsx,js,jsx,mjs,cjs,py,go,rs}"],
|
|
@@ -4788,14 +4918,14 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4788
4918
|
const dependents = [];
|
|
4789
4919
|
const evidence = [];
|
|
4790
4920
|
const importRegex = new RegExp(
|
|
4791
|
-
`(?:import|from|require\\s*\\(
|
|
4921
|
+
`(?:import|from|require\\s*\\()[\\w{}*,.\\s]*['"][^'"]*\\b${baseName}(?:\\.[a-zA-Z0-9]+)?['"]`,
|
|
4792
4922
|
"i"
|
|
4793
4923
|
);
|
|
4794
4924
|
for (const f of files) {
|
|
4795
|
-
if (
|
|
4796
|
-
const fullPath =
|
|
4925
|
+
if (path12.normalize(f) === path12.normalize(relTarget)) continue;
|
|
4926
|
+
const fullPath = path12.join(root, f);
|
|
4797
4927
|
try {
|
|
4798
|
-
const content =
|
|
4928
|
+
const content = fs10.readFileSync(fullPath, "utf-8");
|
|
4799
4929
|
const lines = content.split("\n");
|
|
4800
4930
|
const hitIdx = lines.findIndex((l) => importRegex.test(l));
|
|
4801
4931
|
if (hitIdx >= 0) {
|
|
@@ -4811,7 +4941,7 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4811
4941
|
}
|
|
4812
4942
|
}
|
|
4813
4943
|
async function findRelatedTests(targetFile, root, owningPackage, additionalDependents = [], exactOnly = false) {
|
|
4814
|
-
const normTarget = normalizeScope(
|
|
4944
|
+
const normTarget = normalizeScope(path12.normalize(targetFile)) || path12.normalize(targetFile);
|
|
4815
4945
|
try {
|
|
4816
4946
|
const db = await getDb();
|
|
4817
4947
|
const targetFileId = `file::${normTarget}`;
|
|
@@ -4835,13 +4965,13 @@ async function findRelatedTests(targetFile, root, owningPackage, additionalDepen
|
|
|
4835
4965
|
}
|
|
4836
4966
|
} catch {
|
|
4837
4967
|
}
|
|
4838
|
-
const baseName =
|
|
4968
|
+
const baseName = path12.basename(normTarget, path12.extname(normTarget));
|
|
4839
4969
|
const testFiles = [];
|
|
4840
4970
|
const stems = /* @__PURE__ */ new Set();
|
|
4841
4971
|
if (baseName) stems.add(baseName);
|
|
4842
4972
|
if (!exactOnly) {
|
|
4843
4973
|
for (const dep of additionalDependents.slice(0, 3)) {
|
|
4844
|
-
const depBase =
|
|
4974
|
+
const depBase = path12.basename(dep, path12.extname(dep));
|
|
4845
4975
|
if (depBase) stems.add(depBase);
|
|
4846
4976
|
}
|
|
4847
4977
|
}
|
|
@@ -4877,12 +5007,12 @@ async function findRelatedTests(targetFile, root, owningPackage, additionalDepen
|
|
|
4877
5007
|
);
|
|
4878
5008
|
}
|
|
4879
5009
|
const pkgTests = await fastGlob3(pkgPatterns, {
|
|
4880
|
-
cwd:
|
|
5010
|
+
cwd: path12.join(root, owningPackage.path),
|
|
4881
5011
|
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
4882
5012
|
onlyFiles: true
|
|
4883
5013
|
});
|
|
4884
5014
|
for (const pt of pkgTests) {
|
|
4885
|
-
const fullRel =
|
|
5015
|
+
const fullRel = path12.join(owningPackage.path, pt);
|
|
4886
5016
|
if (!testFiles.includes(fullRel)) {
|
|
4887
5017
|
testFiles.push(fullRel);
|
|
4888
5018
|
}
|
|
@@ -4981,6 +5111,20 @@ async function calculateBlastRadius(target, options = {}) {
|
|
|
4981
5111
|
const nonRuntime = diffShape !== "runtime-code";
|
|
4982
5112
|
const affectedTests = await findRelatedTests(target, root, owningPackage, directDependents, nonRuntime);
|
|
4983
5113
|
let testReasons = /* @__PURE__ */ new Map();
|
|
5114
|
+
if (targetType === "symbol") {
|
|
5115
|
+
try {
|
|
5116
|
+
const { symbolReachability: symbolReachability2 } = await Promise.resolve().then(() => (init_symbolReach(), symbolReach_exports));
|
|
5117
|
+
const reach = await symbolReachability2(target, 10, root);
|
|
5118
|
+
for (const r of reach.rows) {
|
|
5119
|
+
if (!affectedTests.includes(r.test)) {
|
|
5120
|
+
affectedTests.push(r.test);
|
|
5121
|
+
testReasons.set(r.test, `reachability tier ${reach.tier}: ${r.evidence.substring(0, 80)}`);
|
|
5122
|
+
}
|
|
5123
|
+
evidence.push(r.evidence);
|
|
5124
|
+
}
|
|
5125
|
+
} catch {
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
4984
5128
|
try {
|
|
4985
5129
|
const { rankTestsByHistory: rankTestsByHistory2 } = await Promise.resolve().then(() => (init_testHistory(), testHistory_exports));
|
|
4986
5130
|
const ranked = await rankTestsByHistory2(target, affectedTests);
|
|
@@ -6195,27 +6339,27 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
6195
6339
|
if (results.length >= limit || depth > MAX_WALK_DEPTH) return;
|
|
6196
6340
|
let entries = [];
|
|
6197
6341
|
try {
|
|
6198
|
-
entries =
|
|
6342
|
+
entries = fs34.readdirSync(dir);
|
|
6199
6343
|
} catch {
|
|
6200
6344
|
return;
|
|
6201
6345
|
}
|
|
6202
6346
|
for (const entry of entries) {
|
|
6203
6347
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "build") continue;
|
|
6204
|
-
const full =
|
|
6348
|
+
const full = path37.join(dir, entry);
|
|
6205
6349
|
try {
|
|
6206
|
-
const stat =
|
|
6350
|
+
const stat = fs34.statSync(full);
|
|
6207
6351
|
if (stat.isDirectory()) {
|
|
6208
6352
|
walk2(full, depth + 1);
|
|
6209
6353
|
} else if (entry.toLowerCase().includes(query.toLowerCase())) {
|
|
6210
|
-
results.push(
|
|
6354
|
+
results.push(path37.relative(root, full));
|
|
6211
6355
|
}
|
|
6212
6356
|
} catch {
|
|
6213
6357
|
}
|
|
6214
6358
|
}
|
|
6215
6359
|
};
|
|
6216
6360
|
var walk = walk2;
|
|
6217
|
-
const
|
|
6218
|
-
const
|
|
6361
|
+
const fs34 = await import("fs");
|
|
6362
|
+
const path37 = await import("path");
|
|
6219
6363
|
const root = process.cwd();
|
|
6220
6364
|
const results = [];
|
|
6221
6365
|
const MAX_WALK_DEPTH = 8;
|
|
@@ -6348,9 +6492,9 @@ ${summary}`;
|
|
|
6348
6492
|
}
|
|
6349
6493
|
async function codebaseImpactFallback(target) {
|
|
6350
6494
|
try {
|
|
6351
|
-
const { execSync:
|
|
6495
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
6352
6496
|
const root = process.cwd();
|
|
6353
|
-
const grepResult =
|
|
6497
|
+
const grepResult = execSync10(
|
|
6354
6498
|
`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`,
|
|
6355
6499
|
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
6356
6500
|
).trim();
|
|
@@ -6496,9 +6640,9 @@ __export(kumaShared_exports, {
|
|
|
6496
6640
|
isReadTool: () => isReadTool,
|
|
6497
6641
|
isTestTool: () => isTestTool
|
|
6498
6642
|
});
|
|
6499
|
-
import { execSync as
|
|
6500
|
-
import
|
|
6501
|
-
import
|
|
6643
|
+
import { execSync as execSync6 } from "child_process";
|
|
6644
|
+
import fs11 from "fs";
|
|
6645
|
+
import path13 from "path";
|
|
6502
6646
|
function isEditTool(toolName) {
|
|
6503
6647
|
const n = (toolName || "").toLowerCase();
|
|
6504
6648
|
return n.includes("edit") || n.includes("write") || n.includes("patch") || n.includes("apply") || n.includes("replace");
|
|
@@ -6536,7 +6680,7 @@ function getSessionStats(inputGoal) {
|
|
|
6536
6680
|
function getGitDiffStat(timeout = 3e3) {
|
|
6537
6681
|
try {
|
|
6538
6682
|
const root = getProjectRoot();
|
|
6539
|
-
return
|
|
6683
|
+
return execSync6("git diff --stat", {
|
|
6540
6684
|
cwd: root,
|
|
6541
6685
|
encoding: "utf-8",
|
|
6542
6686
|
timeout
|
|
@@ -6549,7 +6693,7 @@ function getUncommittedFiles(timeout = 3e3) {
|
|
|
6549
6693
|
const files = /* @__PURE__ */ new Set();
|
|
6550
6694
|
try {
|
|
6551
6695
|
const root = getProjectRoot();
|
|
6552
|
-
const porcelain =
|
|
6696
|
+
const porcelain = execSync6("git status --porcelain", {
|
|
6553
6697
|
cwd: root,
|
|
6554
6698
|
encoding: "utf-8",
|
|
6555
6699
|
timeout,
|
|
@@ -6568,17 +6712,17 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6568
6712
|
let base = "";
|
|
6569
6713
|
try {
|
|
6570
6714
|
const root = getProjectRoot();
|
|
6571
|
-
const branch =
|
|
6715
|
+
const branch = execSync6("git rev-parse --abbrev-ref HEAD", {
|
|
6572
6716
|
cwd: root,
|
|
6573
6717
|
encoding: "utf-8",
|
|
6574
6718
|
timeout,
|
|
6575
6719
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6576
6720
|
}).trim();
|
|
6577
|
-
const baseRef =
|
|
6721
|
+
const baseRef = execSync6(
|
|
6578
6722
|
`git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo origin/${branch === "main" || branch === "master" ? "HEAD" : "main"}`,
|
|
6579
6723
|
{ cwd: root, encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }
|
|
6580
6724
|
).trim().replace("refs/remotes/", "");
|
|
6581
|
-
const mergeBase =
|
|
6725
|
+
const mergeBase = execSync6(`git merge-base HEAD ${baseRef} 2>/dev/null || git rev-list --max-parents=0 HEAD`, {
|
|
6582
6726
|
cwd: root,
|
|
6583
6727
|
encoding: "utf-8",
|
|
6584
6728
|
timeout,
|
|
@@ -6586,7 +6730,7 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6586
6730
|
}).trim().split("\n")[0];
|
|
6587
6731
|
base = baseRef;
|
|
6588
6732
|
if (mergeBase) {
|
|
6589
|
-
const out =
|
|
6733
|
+
const out = execSync6(`git diff --name-only ${mergeBase} HEAD`, {
|
|
6590
6734
|
cwd: root,
|
|
6591
6735
|
encoding: "utf-8",
|
|
6592
6736
|
timeout,
|
|
@@ -6604,10 +6748,10 @@ function hasVerificationEvidence(toolCalls) {
|
|
|
6604
6748
|
}
|
|
6605
6749
|
function hasVerificationInHookLog(hours = 24) {
|
|
6606
6750
|
try {
|
|
6607
|
-
const fp =
|
|
6608
|
-
if (!
|
|
6751
|
+
const fp = path13.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
6752
|
+
if (!fs11.existsSync(fp)) return false;
|
|
6609
6753
|
const cutoff = Date.now() - hours * 60 * 60 * 1e3;
|
|
6610
|
-
const lines =
|
|
6754
|
+
const lines = fs11.readFileSync(fp, "utf-8").split("\n");
|
|
6611
6755
|
for (const line of lines) {
|
|
6612
6756
|
if (!line.trim()) continue;
|
|
6613
6757
|
try {
|
|
@@ -6710,7 +6854,7 @@ function detectTestWeakening(files, root) {
|
|
|
6710
6854
|
for (const f of files) {
|
|
6711
6855
|
if (!TEST_FILE_PATTERN.test(f)) continue;
|
|
6712
6856
|
try {
|
|
6713
|
-
const diff =
|
|
6857
|
+
const diff = execSync6(`git diff --unified=0 -- "${f}"`, {
|
|
6714
6858
|
cwd,
|
|
6715
6859
|
encoding: "utf-8",
|
|
6716
6860
|
timeout: 4e3,
|
|
@@ -6749,19 +6893,19 @@ __export(kumaMemory_exports, {
|
|
|
6749
6893
|
scoreMemoryRelevance: () => scoreMemoryRelevance,
|
|
6750
6894
|
shouldRecordDecision: () => shouldRecordDecision
|
|
6751
6895
|
});
|
|
6752
|
-
import
|
|
6753
|
-
import
|
|
6896
|
+
import fs12 from "fs";
|
|
6897
|
+
import path14 from "path";
|
|
6754
6898
|
function scoreMemoryRelevance(context, limit = 5) {
|
|
6755
6899
|
const results = [];
|
|
6756
|
-
const kumaDir =
|
|
6757
|
-
if (!
|
|
6900
|
+
const kumaDir = path14.join(getProjectRoot(), MEMORY_DIR);
|
|
6901
|
+
if (!fs12.existsSync(kumaDir)) return [];
|
|
6758
6902
|
const terms = Array.from(new Set(context.toLowerCase().split(/[\s/._-]+/).filter((w) => w.length > 2)));
|
|
6759
6903
|
if (terms.length === 0) return [];
|
|
6760
6904
|
try {
|
|
6761
|
-
const files =
|
|
6905
|
+
const files = fs12.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
|
|
6762
6906
|
for (const file of files) {
|
|
6763
6907
|
try {
|
|
6764
|
-
const content =
|
|
6908
|
+
const content = fs12.readFileSync(path14.join(kumaDir, file), "utf-8");
|
|
6765
6909
|
const lower = content.toLowerCase();
|
|
6766
6910
|
let matchCount = 0;
|
|
6767
6911
|
for (const term of terms) {
|
|
@@ -6943,8 +7087,8 @@ __export(cacheFreshness_exports, {
|
|
|
6943
7087
|
isStale: () => isStale,
|
|
6944
7088
|
markDirty: () => markDirty
|
|
6945
7089
|
});
|
|
6946
|
-
import
|
|
6947
|
-
import
|
|
7090
|
+
import fs13 from "fs";
|
|
7091
|
+
import path15 from "path";
|
|
6948
7092
|
function isStale(savedAtMs, ttlMs, nowMs = Date.now()) {
|
|
6949
7093
|
if (!savedAtMs) return true;
|
|
6950
7094
|
return nowMs - savedAtMs > ttlMs;
|
|
@@ -6957,13 +7101,13 @@ function formatAge(savedAtMs, nowMs = Date.now()) {
|
|
|
6957
7101
|
return `${Math.floor(s / 86400)}d`;
|
|
6958
7102
|
}
|
|
6959
7103
|
function dirtyPath() {
|
|
6960
|
-
return
|
|
7104
|
+
return path15.join(getProjectRoot(), DIRTY_FILE);
|
|
6961
7105
|
}
|
|
6962
7106
|
function loadDirty() {
|
|
6963
7107
|
try {
|
|
6964
7108
|
const fp = dirtyPath();
|
|
6965
|
-
if (
|
|
6966
|
-
const parsed = JSON.parse(
|
|
7109
|
+
if (fs13.existsSync(fp)) {
|
|
7110
|
+
const parsed = JSON.parse(fs13.readFileSync(fp, "utf-8"));
|
|
6967
7111
|
if (parsed && typeof parsed === "object") return parsed;
|
|
6968
7112
|
}
|
|
6969
7113
|
} catch {
|
|
@@ -6973,14 +7117,14 @@ function loadDirty() {
|
|
|
6973
7117
|
function markDirty(filePath) {
|
|
6974
7118
|
try {
|
|
6975
7119
|
const fp = dirtyPath();
|
|
6976
|
-
const dir =
|
|
6977
|
-
if (!
|
|
7120
|
+
const dir = path15.dirname(fp);
|
|
7121
|
+
if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
|
|
6978
7122
|
const dirty = loadDirty();
|
|
6979
7123
|
dirty[filePath] = Date.now();
|
|
6980
7124
|
const keys = Object.keys(dirty).sort((a, b) => dirty[b] - dirty[a]).slice(0, 200);
|
|
6981
7125
|
const bounded = {};
|
|
6982
7126
|
for (const k of keys) bounded[k] = dirty[k];
|
|
6983
|
-
|
|
7127
|
+
fs13.writeFileSync(fp, JSON.stringify(bounded), "utf-8");
|
|
6984
7128
|
} catch {
|
|
6985
7129
|
}
|
|
6986
7130
|
}
|
|
@@ -6990,7 +7134,7 @@ function getDirtyFiles() {
|
|
|
6990
7134
|
function clearDirty() {
|
|
6991
7135
|
try {
|
|
6992
7136
|
const fp = dirtyPath();
|
|
6993
|
-
if (
|
|
7137
|
+
if (fs13.existsSync(fp)) fs13.rmSync(fp, { force: true });
|
|
6994
7138
|
} catch {
|
|
6995
7139
|
}
|
|
6996
7140
|
}
|
|
@@ -7010,8 +7154,8 @@ var codeReuseEngine_exports = {};
|
|
|
7010
7154
|
__export(codeReuseEngine_exports, {
|
|
7011
7155
|
findReusableSymbols: () => findReusableSymbols
|
|
7012
7156
|
});
|
|
7013
|
-
import
|
|
7014
|
-
import
|
|
7157
|
+
import path16 from "path";
|
|
7158
|
+
import fs14 from "fs";
|
|
7015
7159
|
async function findReusableSymbols(query, scope) {
|
|
7016
7160
|
const cleanQuery = query.trim().toLowerCase();
|
|
7017
7161
|
const searchTokens = cleanQuery.replace(/[^a-zA-Z0-9_\s]/g, " ").split(/\s+/).filter((t) => t.length >= 2);
|
|
@@ -7134,7 +7278,7 @@ function detectRecommendedUtilDirectory() {
|
|
|
7134
7278
|
"utils"
|
|
7135
7279
|
];
|
|
7136
7280
|
for (const c of candidates) {
|
|
7137
|
-
if (
|
|
7281
|
+
if (fs14.existsSync(path16.resolve(root, c))) {
|
|
7138
7282
|
return c;
|
|
7139
7283
|
}
|
|
7140
7284
|
}
|
|
@@ -7153,7 +7297,7 @@ var domainClusterEngine_exports = {};
|
|
|
7153
7297
|
__export(domainClusterEngine_exports, {
|
|
7154
7298
|
buildSubsystemClusters: () => buildSubsystemClusters
|
|
7155
7299
|
});
|
|
7156
|
-
import
|
|
7300
|
+
import path17 from "path";
|
|
7157
7301
|
async function buildSubsystemClusters() {
|
|
7158
7302
|
const db = await getDb();
|
|
7159
7303
|
const fileStmt = db.prepare(`
|
|
@@ -7230,7 +7374,7 @@ async function buildSubsystemClusters() {
|
|
|
7230
7374
|
for (const [cKey, files] of clusterFileMap.entries()) {
|
|
7231
7375
|
for (const f of files) {
|
|
7232
7376
|
fileToCluster.set(f, cKey);
|
|
7233
|
-
fileToCluster.set(
|
|
7377
|
+
fileToCluster.set(path17.normalize(f), cKey);
|
|
7234
7378
|
}
|
|
7235
7379
|
}
|
|
7236
7380
|
const clusterInteractions = /* @__PURE__ */ new Map();
|
|
@@ -7238,8 +7382,8 @@ async function buildSubsystemClusters() {
|
|
|
7238
7382
|
const srcFile = extractFilePathFromNodeId(e.source);
|
|
7239
7383
|
const tgtFile = extractFilePathFromNodeId(e.target);
|
|
7240
7384
|
if (!srcFile || !tgtFile) continue;
|
|
7241
|
-
const srcCluster = fileToCluster.get(srcFile) || fileToCluster.get(
|
|
7242
|
-
const tgtCluster = fileToCluster.get(tgtFile) || fileToCluster.get(
|
|
7385
|
+
const srcCluster = fileToCluster.get(srcFile) || fileToCluster.get(path17.normalize(srcFile));
|
|
7386
|
+
const tgtCluster = fileToCluster.get(tgtFile) || fileToCluster.get(path17.normalize(tgtFile));
|
|
7243
7387
|
if (srcCluster && tgtCluster && srcCluster !== tgtCluster) {
|
|
7244
7388
|
const pairKey = `${srcCluster}->${tgtCluster}`;
|
|
7245
7389
|
const stats = clusterInteractions.get(pairKey) || { calls: 0, imports: 0 };
|
|
@@ -7261,13 +7405,13 @@ async function buildSubsystemClusters() {
|
|
|
7261
7405
|
const clusterId = nodeId("feature_domain", cKey);
|
|
7262
7406
|
const name = formatClusterName(cKey);
|
|
7263
7407
|
const role = inferSubsystemRole(cKey, files);
|
|
7264
|
-
const entrypoints = files.filter((f) => /index|main|server|app|engine|root/i.test(
|
|
7408
|
+
const entrypoints = files.filter((f) => /index|main|server|app|engine|root/i.test(path17.basename(f))).slice(0, 3);
|
|
7265
7409
|
if (entrypoints.length === 0 && files.length > 0) {
|
|
7266
7410
|
entrypoints.push(files[0]);
|
|
7267
7411
|
}
|
|
7268
7412
|
const allSyms = [];
|
|
7269
7413
|
for (const f of files) {
|
|
7270
|
-
const syms = fileSymbols.get(f) || fileSymbols.get(
|
|
7414
|
+
const syms = fileSymbols.get(f) || fileSymbols.get(path17.normalize(f)) || [];
|
|
7271
7415
|
for (const s of syms) {
|
|
7272
7416
|
if (!allSyms.includes(s)) allSyms.push(s);
|
|
7273
7417
|
}
|
|
@@ -7446,7 +7590,7 @@ function inferSubsystemRole(key, files) {
|
|
|
7446
7590
|
if (k.includes("util") || k.includes("common")) {
|
|
7447
7591
|
return "Shared path validation, configuration loading, and platform detection helpers.";
|
|
7448
7592
|
}
|
|
7449
|
-
return `Core subsystem handling ${files.slice(0, 3).map((f) =>
|
|
7593
|
+
return `Core subsystem handling ${files.slice(0, 3).map((f) => path17.basename(f)).join(", ")}.`;
|
|
7450
7594
|
}
|
|
7451
7595
|
function inferTypedVerb(sourceKey, targetKey, _stats) {
|
|
7452
7596
|
const src = sourceKey.toLowerCase();
|
|
@@ -7525,18 +7669,18 @@ __export(domainRules_exports, {
|
|
|
7525
7669
|
readLayer: () => readLayer,
|
|
7526
7670
|
writeLayer: () => writeLayer
|
|
7527
7671
|
});
|
|
7528
|
-
import
|
|
7529
|
-
import
|
|
7672
|
+
import fs15 from "fs";
|
|
7673
|
+
import path18 from "path";
|
|
7530
7674
|
function layerFilePath(layer) {
|
|
7531
|
-
return
|
|
7675
|
+
return path18.join(getProjectRoot(), ".kuma", LAYER_FILES[layer].filename);
|
|
7532
7676
|
}
|
|
7533
7677
|
function ensureLayerFile(layer) {
|
|
7534
7678
|
const fp = layerFilePath(layer);
|
|
7535
|
-
if (!
|
|
7679
|
+
if (!fs15.existsSync(fp)) {
|
|
7536
7680
|
const template = layer === "arch_flow" ? ARCH_FLOW_TEMPLATE : GOTCHAS_TEMPLATE;
|
|
7537
|
-
const dir =
|
|
7538
|
-
if (!
|
|
7539
|
-
|
|
7681
|
+
const dir = path18.dirname(fp);
|
|
7682
|
+
if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
|
|
7683
|
+
fs15.writeFileSync(fp, template, "utf-8");
|
|
7540
7684
|
console.error(`[DomainRules] Auto-created ${LAYER_FILES[layer].filename}`);
|
|
7541
7685
|
}
|
|
7542
7686
|
return fp;
|
|
@@ -7544,7 +7688,7 @@ function ensureLayerFile(layer) {
|
|
|
7544
7688
|
function readLayer(layer) {
|
|
7545
7689
|
try {
|
|
7546
7690
|
const fp = ensureLayerFile(layer);
|
|
7547
|
-
return
|
|
7691
|
+
return fs15.readFileSync(fp, "utf-8");
|
|
7548
7692
|
} catch (err) {
|
|
7549
7693
|
return `Error reading ${layer}: ${err}`;
|
|
7550
7694
|
}
|
|
@@ -7552,7 +7696,7 @@ function readLayer(layer) {
|
|
|
7552
7696
|
function writeLayer(layer, content) {
|
|
7553
7697
|
try {
|
|
7554
7698
|
const fp = ensureLayerFile(layer);
|
|
7555
|
-
|
|
7699
|
+
fs15.writeFileSync(fp, content, "utf-8");
|
|
7556
7700
|
const label = LAYER_FILES[layer].label;
|
|
7557
7701
|
sessionMemory.recordToolCall("kuma_memory", { action: `write_${layer}` });
|
|
7558
7702
|
return `\u2705 **${label}** updated successfully.`;
|
|
@@ -7563,9 +7707,9 @@ function writeLayer(layer, content) {
|
|
|
7563
7707
|
function appendToLayer(layer, entry) {
|
|
7564
7708
|
try {
|
|
7565
7709
|
const fp = ensureLayerFile(layer);
|
|
7566
|
-
const existing =
|
|
7710
|
+
const existing = fs15.readFileSync(fp, "utf-8");
|
|
7567
7711
|
const newContent = existing.trimEnd() + "\n\n" + entry.trim() + "\n";
|
|
7568
|
-
|
|
7712
|
+
fs15.writeFileSync(fp, newContent, "utf-8");
|
|
7569
7713
|
return `\u2705 Entry added to **${LAYER_FILES[layer].label}**.`;
|
|
7570
7714
|
} catch (err) {
|
|
7571
7715
|
return `\u274C Failed to append: ${err}`;
|
|
@@ -7575,7 +7719,7 @@ function getActiveGotchas() {
|
|
|
7575
7719
|
const results = [];
|
|
7576
7720
|
try {
|
|
7577
7721
|
const fp = ensureLayerFile("gotcha");
|
|
7578
|
-
const content =
|
|
7722
|
+
const content = fs15.readFileSync(fp, "utf-8");
|
|
7579
7723
|
const lines = content.split("\n");
|
|
7580
7724
|
let currentSection = "";
|
|
7581
7725
|
let currentFilePath = "";
|
|
@@ -7611,9 +7755,9 @@ function getActiveGotchas() {
|
|
|
7611
7755
|
function checkFileGotchas(filePath) {
|
|
7612
7756
|
const gotchas = getActiveGotchas();
|
|
7613
7757
|
const warnings = [];
|
|
7614
|
-
const base =
|
|
7758
|
+
const base = path18.basename(filePath);
|
|
7615
7759
|
for (const g of gotchas) {
|
|
7616
|
-
const gBase =
|
|
7760
|
+
const gBase = path18.basename(g.filePath);
|
|
7617
7761
|
if (filePath.includes(g.filePath) || g.filePath.includes(filePath) || base && gBase && base === gBase) {
|
|
7618
7762
|
const icon = g.severity === "critical" ? "\u{1F534}" : g.severity === "high" ? "\u{1F7E0}" : g.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
7619
7763
|
warnings.push(`${icon} **Gotcha**: ${g.description} (${g.severity})`);
|
|
@@ -7713,8 +7857,8 @@ __export(kumaInject_exports, {
|
|
|
7713
7857
|
parseHookInput: () => parseHookInput,
|
|
7714
7858
|
trackFileEditLoop: () => trackFileEditLoop
|
|
7715
7859
|
});
|
|
7716
|
-
import
|
|
7717
|
-
import
|
|
7860
|
+
import fs16 from "fs";
|
|
7861
|
+
import path19 from "path";
|
|
7718
7862
|
function hashFileContent(filePath) {
|
|
7719
7863
|
return hashFile(filePath);
|
|
7720
7864
|
}
|
|
@@ -7725,10 +7869,10 @@ function isGotchaFresh(storedHash, currentHash) {
|
|
|
7725
7869
|
}
|
|
7726
7870
|
async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false) {
|
|
7727
7871
|
const root = getProjectRoot();
|
|
7728
|
-
const base =
|
|
7872
|
+
const base = path19.basename(filePath);
|
|
7729
7873
|
try {
|
|
7730
|
-
const dbFile =
|
|
7731
|
-
if (
|
|
7874
|
+
const dbFile = path19.join(root, ".kuma", "kuma.db");
|
|
7875
|
+
if (fs16.existsSync(dbFile)) {
|
|
7732
7876
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7733
7877
|
const db = await getDb2();
|
|
7734
7878
|
const stmt = db.prepare(`
|
|
@@ -7769,11 +7913,11 @@ async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false)
|
|
|
7769
7913
|
} catch {
|
|
7770
7914
|
}
|
|
7771
7915
|
try {
|
|
7772
|
-
if (!
|
|
7916
|
+
if (!fs16.existsSync(path19.join(root, ".kuma"))) return [];
|
|
7773
7917
|
const { getActiveGotchas: getActiveGotchas2 } = await Promise.resolve().then(() => (init_domainRules(), domainRules_exports));
|
|
7774
7918
|
const allGotchas = getActiveGotchas2();
|
|
7775
7919
|
const matched = allGotchas.filter(
|
|
7776
|
-
(g) => g.filePath.includes(filePath) || filePath.includes(g.filePath) || base &&
|
|
7920
|
+
(g) => g.filePath.includes(filePath) || filePath.includes(g.filePath) || base && path19.basename(g.filePath) === base
|
|
7777
7921
|
);
|
|
7778
7922
|
if (matched.length > 0) {
|
|
7779
7923
|
return matched.slice(0, limit).map((g) => ({
|
|
@@ -7792,8 +7936,8 @@ async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false)
|
|
|
7792
7936
|
async function getFileTrace(filePath, limit = 8) {
|
|
7793
7937
|
try {
|
|
7794
7938
|
const root = getProjectRoot();
|
|
7795
|
-
const dbFile =
|
|
7796
|
-
if (!
|
|
7939
|
+
const dbFile = path19.join(root, ".kuma", "kuma.db");
|
|
7940
|
+
if (!fs16.existsSync(dbFile)) return [];
|
|
7797
7941
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7798
7942
|
const db = await getDb2();
|
|
7799
7943
|
const stmt = db.prepare(`
|
|
@@ -7803,7 +7947,7 @@ async function getFileTrace(filePath, limit = 8) {
|
|
|
7803
7947
|
ORDER BY cl.created_at DESC
|
|
7804
7948
|
LIMIT ?
|
|
7805
7949
|
`);
|
|
7806
|
-
stmt.bind([`%${
|
|
7950
|
+
stmt.bind([`%${path19.basename(filePath)}%`, limit]);
|
|
7807
7951
|
const rows = [];
|
|
7808
7952
|
while (stmt.step()) rows.push(stmt.getAsObject());
|
|
7809
7953
|
stmt.free();
|
|
@@ -7821,7 +7965,7 @@ async function getFileTrace(filePath, limit = 8) {
|
|
|
7821
7965
|
function formatFileTrace(entries, filePath) {
|
|
7822
7966
|
if (entries.length === 0) return "";
|
|
7823
7967
|
const lines = [
|
|
7824
|
-
`\u{1F570}\uFE0F **Why is ${
|
|
7968
|
+
`\u{1F570}\uFE0F **Why is ${path19.basename(filePath)} written this way?** \u2014 ${entries.length} change(s) recorded:`
|
|
7825
7969
|
];
|
|
7826
7970
|
for (const e of entries) {
|
|
7827
7971
|
const date = new Date((e.createdAt || 0) * 1e3).toLocaleDateString("en-US", {
|
|
@@ -7837,12 +7981,12 @@ function formatFileTrace(entries, filePath) {
|
|
|
7837
7981
|
async function getDecisionsForFile(filePath, limit = 3) {
|
|
7838
7982
|
const decisions = [];
|
|
7839
7983
|
const root = getProjectRoot();
|
|
7840
|
-
const base =
|
|
7984
|
+
const base = path19.basename(filePath);
|
|
7841
7985
|
const baseWithoutExt = base.replace(/\.[^.]+$/, "");
|
|
7842
7986
|
try {
|
|
7843
|
-
const decisionsMdPath =
|
|
7844
|
-
if (
|
|
7845
|
-
const content =
|
|
7987
|
+
const decisionsMdPath = path19.join(root, ".kuma", "memories", "decisions.md");
|
|
7988
|
+
if (fs16.existsSync(decisionsMdPath)) {
|
|
7989
|
+
const content = fs16.readFileSync(decisionsMdPath, "utf-8");
|
|
7846
7990
|
const sections = content.split(/^##\s+/m);
|
|
7847
7991
|
for (const section of sections) {
|
|
7848
7992
|
if (!section.trim() || section.startsWith("# ")) continue;
|
|
@@ -7947,13 +8091,13 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7947
8091
|
if (traceStr) parts.push(traceStr);
|
|
7948
8092
|
try {
|
|
7949
8093
|
const root = getProjectRoot();
|
|
7950
|
-
if (
|
|
8094
|
+
if (fs16.existsSync(path19.join(root, ".kuma", "kuma.db"))) {
|
|
7951
8095
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7952
8096
|
const db = await getDb2();
|
|
7953
8097
|
const stmt = db.prepare(
|
|
7954
8098
|
`SELECT COUNT(*) FROM known_gotchas WHERE status = 'resolved' AND file_path LIKE ?`
|
|
7955
8099
|
);
|
|
7956
|
-
stmt.bind([`%${
|
|
8100
|
+
stmt.bind([`%${path19.basename(filePath)}%`]);
|
|
7957
8101
|
const found = stmt.step();
|
|
7958
8102
|
const count = found ? Number(stmt.getAsObject()["COUNT(*)"]) : 0;
|
|
7959
8103
|
stmt.free();
|
|
@@ -7964,7 +8108,7 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7964
8108
|
} catch {
|
|
7965
8109
|
}
|
|
7966
8110
|
if (gotchas.length > 0) {
|
|
7967
|
-
const scopeHint =
|
|
8111
|
+
const scopeHint = path19.basename(path19.dirname(filePath)) || "current change";
|
|
7968
8112
|
parts.push(
|
|
7969
8113
|
`\u2705 AFTER editing, verify: kuma_safety({ action: "verify", scope: "${scopeHint}" })`
|
|
7970
8114
|
);
|
|
@@ -7980,11 +8124,11 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7980
8124
|
async function getFileSummary2(filePath) {
|
|
7981
8125
|
try {
|
|
7982
8126
|
const root = getProjectRoot();
|
|
7983
|
-
const dbFile =
|
|
7984
|
-
if (!
|
|
8127
|
+
const dbFile = path19.join(root, ".kuma", "kuma.db");
|
|
8128
|
+
if (!fs16.existsSync(dbFile)) return null;
|
|
7985
8129
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7986
8130
|
const db = await getDb2();
|
|
7987
|
-
const base =
|
|
8131
|
+
const base = path19.basename(filePath);
|
|
7988
8132
|
const stmt = db.prepare(`
|
|
7989
8133
|
SELECT
|
|
7990
8134
|
(SELECT COUNT(*) FROM known_gotchas WHERE status = 'active' AND (file_path = ? OR file_path LIKE ?)) as active_gotchas,
|
|
@@ -8018,9 +8162,9 @@ function parseHookInput(raw) {
|
|
|
8018
8162
|
const root = data && typeof data === "object" ? data : {};
|
|
8019
8163
|
const ti = root.tool_input && typeof root.tool_input === "object" ? root.tool_input : {};
|
|
8020
8164
|
const args = root.args && typeof root.args === "object" ? root.args : {};
|
|
8021
|
-
const
|
|
8165
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
8022
8166
|
const add = (p) => {
|
|
8023
|
-
if (typeof p === "string" && p.trim())
|
|
8167
|
+
if (typeof p === "string" && p.trim()) paths2.add(p.trim());
|
|
8024
8168
|
};
|
|
8025
8169
|
add(ti.file_path);
|
|
8026
8170
|
add(ti.new_file_path);
|
|
@@ -8038,7 +8182,7 @@ function parseHookInput(raw) {
|
|
|
8038
8182
|
add(args.file);
|
|
8039
8183
|
const cmd = (typeof ti.command === "string" ? ti.command : void 0) ?? (typeof root.command === "string" ? root.command : void 0) ?? (typeof root.commandline === "string" ? root.commandline : void 0) ?? (typeof args.command === "string" ? args.command : void 0);
|
|
8040
8184
|
const goal = (typeof ti.goal === "string" ? ti.goal : void 0) ?? (typeof root.goal === "string" ? root.goal : void 0);
|
|
8041
|
-
const out = { filePaths: [...
|
|
8185
|
+
const out = { filePaths: [...paths2] };
|
|
8042
8186
|
if (goal) out.goal = goal;
|
|
8043
8187
|
if (cmd) out.command = cmd;
|
|
8044
8188
|
return out;
|
|
@@ -8076,9 +8220,9 @@ async function getCommandContext(command) {
|
|
|
8076
8220
|
}
|
|
8077
8221
|
function loadHookState(root) {
|
|
8078
8222
|
try {
|
|
8079
|
-
const fp =
|
|
8080
|
-
if (
|
|
8081
|
-
return JSON.parse(
|
|
8223
|
+
const fp = path19.join(root, HOOK_STATE_FILE);
|
|
8224
|
+
if (fs16.existsSync(fp)) {
|
|
8225
|
+
return JSON.parse(fs16.readFileSync(fp, "utf-8"));
|
|
8082
8226
|
}
|
|
8083
8227
|
} catch {
|
|
8084
8228
|
}
|
|
@@ -8086,10 +8230,10 @@ function loadHookState(root) {
|
|
|
8086
8230
|
}
|
|
8087
8231
|
function saveHookState(root, state) {
|
|
8088
8232
|
try {
|
|
8089
|
-
const fp =
|
|
8090
|
-
const dir =
|
|
8091
|
-
if (!
|
|
8092
|
-
|
|
8233
|
+
const fp = path19.join(root, HOOK_STATE_FILE);
|
|
8234
|
+
const dir = path19.dirname(fp);
|
|
8235
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
8236
|
+
fs16.writeFileSync(fp, JSON.stringify(state), "utf-8");
|
|
8093
8237
|
} catch {
|
|
8094
8238
|
}
|
|
8095
8239
|
}
|
|
@@ -8123,7 +8267,7 @@ async function trackFileEditLoop(filePath) {
|
|
|
8123
8267
|
saveHookState(root, state);
|
|
8124
8268
|
if (recorded) {
|
|
8125
8269
|
try {
|
|
8126
|
-
if (!
|
|
8270
|
+
if (!fs16.existsSync(path19.join(root, ".kuma", "kuma.db"))) return entry.count;
|
|
8127
8271
|
const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
8128
8272
|
await addGotcha2({
|
|
8129
8273
|
filePath,
|
|
@@ -8177,8 +8321,8 @@ __export(kumaGotchas_exports, {
|
|
|
8177
8321
|
syncGotchasToDb: () => syncGotchasToDb,
|
|
8178
8322
|
tokenizeForSimilarity: () => tokenizeForSimilarity
|
|
8179
8323
|
});
|
|
8180
|
-
import
|
|
8181
|
-
import
|
|
8324
|
+
import fs17 from "fs";
|
|
8325
|
+
import path20 from "path";
|
|
8182
8326
|
async function ensureGotchasSchema() {
|
|
8183
8327
|
const db = await getDb();
|
|
8184
8328
|
db.exec(`
|
|
@@ -8600,8 +8744,8 @@ async function getActiveGotchasForCommand(command) {
|
|
|
8600
8744
|
}
|
|
8601
8745
|
async function recordInjection(params) {
|
|
8602
8746
|
try {
|
|
8603
|
-
const fp =
|
|
8604
|
-
if (!
|
|
8747
|
+
const fp = path20.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
8748
|
+
if (!fs17.existsSync(path20.dirname(fp))) fs17.mkdirSync(path20.dirname(fp), { recursive: true });
|
|
8605
8749
|
const line = JSON.stringify({
|
|
8606
8750
|
filePath: params.filePath || null,
|
|
8607
8751
|
command: params.command || null,
|
|
@@ -8610,16 +8754,16 @@ async function recordInjection(params) {
|
|
|
8610
8754
|
// conservative estimate: avoided re-research / re-discovery
|
|
8611
8755
|
at: Date.now()
|
|
8612
8756
|
});
|
|
8613
|
-
|
|
8757
|
+
fs17.appendFileSync(fp, line + "\n", "utf-8");
|
|
8614
8758
|
} catch {
|
|
8615
8759
|
}
|
|
8616
8760
|
}
|
|
8617
8761
|
function getInjectionStats(hours = 24) {
|
|
8618
8762
|
try {
|
|
8619
|
-
const fp =
|
|
8620
|
-
if (!
|
|
8763
|
+
const fp = path20.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
8764
|
+
if (!fs17.existsSync(fp)) return { count: 0, savedMs: 0 };
|
|
8621
8765
|
const cutoff = Date.now() - hours * 60 * 60 * 1e3;
|
|
8622
|
-
const lines =
|
|
8766
|
+
const lines = fs17.readFileSync(fp, "utf-8").split("\n");
|
|
8623
8767
|
let count = 0;
|
|
8624
8768
|
let savedMs = 0;
|
|
8625
8769
|
for (const line of lines) {
|
|
@@ -8677,9 +8821,9 @@ async function getGotchasByStatus(status, limit = 20) {
|
|
|
8677
8821
|
}
|
|
8678
8822
|
function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
8679
8823
|
try {
|
|
8680
|
-
const mdPath =
|
|
8681
|
-
if (!
|
|
8682
|
-
const content =
|
|
8824
|
+
const mdPath = path20.join(getProjectRoot(), ".kuma", "KNOWN_GOTCHAS.md");
|
|
8825
|
+
if (!fs17.existsSync(mdPath)) return false;
|
|
8826
|
+
const content = fs17.readFileSync(mdPath, "utf-8");
|
|
8683
8827
|
const lines = content.split("\n");
|
|
8684
8828
|
const normalizedTargets = targetPaths.map((p) => p.trim().toLowerCase()).filter(Boolean);
|
|
8685
8829
|
if (normalizedTargets.length === 0) return false;
|
|
@@ -8696,9 +8840,9 @@ function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
|
8696
8840
|
modified = true;
|
|
8697
8841
|
}
|
|
8698
8842
|
const headingFile = headingMatch[1].trim().toLowerCase();
|
|
8699
|
-
const headingBase =
|
|
8843
|
+
const headingBase = path20.basename(headingFile);
|
|
8700
8844
|
inMatchingSection = normalizedTargets.some(
|
|
8701
|
-
(t) => headingFile.includes(t) || t.includes(headingFile) || headingBase === t || headingBase ===
|
|
8845
|
+
(t) => headingFile.includes(t) || t.includes(headingFile) || headingBase === t || headingBase === path20.basename(t)
|
|
8702
8846
|
);
|
|
8703
8847
|
sectionHasStatus = false;
|
|
8704
8848
|
outputLines.push(line);
|
|
@@ -8720,7 +8864,7 @@ function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
|
8720
8864
|
modified = true;
|
|
8721
8865
|
}
|
|
8722
8866
|
if (modified) {
|
|
8723
|
-
|
|
8867
|
+
fs17.writeFileSync(mdPath, outputLines.join("\n"), "utf-8");
|
|
8724
8868
|
}
|
|
8725
8869
|
return modified;
|
|
8726
8870
|
} catch {
|
|
@@ -8861,24 +9005,24 @@ var kumaMaintenance_exports = {};
|
|
|
8861
9005
|
__export(kumaMaintenance_exports, {
|
|
8862
9006
|
maybeRunAutoCleanup: () => maybeRunAutoCleanup
|
|
8863
9007
|
});
|
|
8864
|
-
import
|
|
8865
|
-
import
|
|
9008
|
+
import fs18 from "fs";
|
|
9009
|
+
import path21 from "path";
|
|
8866
9010
|
async function maybeRunAutoCleanup() {
|
|
8867
9011
|
try {
|
|
8868
9012
|
const root = getProjectRoot();
|
|
8869
|
-
const stateFile =
|
|
9013
|
+
const stateFile = path21.join(root, GC_STATE_FILE);
|
|
8870
9014
|
const now = Date.now();
|
|
8871
9015
|
let lastRun = 0;
|
|
8872
|
-
if (
|
|
9016
|
+
if (fs18.existsSync(stateFile)) {
|
|
8873
9017
|
try {
|
|
8874
|
-
lastRun = JSON.parse(
|
|
9018
|
+
lastRun = JSON.parse(fs18.readFileSync(stateFile, "utf-8")).lastRun || 0;
|
|
8875
9019
|
} catch {
|
|
8876
9020
|
lastRun = 0;
|
|
8877
9021
|
}
|
|
8878
9022
|
}
|
|
8879
9023
|
if (now - lastRun < GC_INTERVAL_MS) return "";
|
|
8880
|
-
|
|
8881
|
-
|
|
9024
|
+
fs18.mkdirSync(path21.dirname(stateFile), { recursive: true });
|
|
9025
|
+
fs18.writeFileSync(stateFile, JSON.stringify({ lastRun: now }), "utf-8");
|
|
8882
9026
|
const { syncGotchasToDb: syncGotchasToDb2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
8883
9027
|
await syncGotchasToDb2();
|
|
8884
9028
|
const { runGarbageCollection: runGarbageCollection2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
@@ -8914,22 +9058,22 @@ __export(kumaPathRules_exports, {
|
|
|
8914
9058
|
loadPathRules: () => loadPathRules,
|
|
8915
9059
|
matchRulePath: () => matchRulePath
|
|
8916
9060
|
});
|
|
8917
|
-
import
|
|
8918
|
-
import
|
|
9061
|
+
import fs19 from "fs";
|
|
9062
|
+
import path22 from "path";
|
|
8919
9063
|
function rulesDir() {
|
|
8920
|
-
return
|
|
9064
|
+
return path22.join(getProjectRoot(), RULES_DIR);
|
|
8921
9065
|
}
|
|
8922
9066
|
function ensureRulesDir() {
|
|
8923
9067
|
try {
|
|
8924
9068
|
const dir = rulesDir();
|
|
8925
|
-
if (!
|
|
9069
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
8926
9070
|
} catch {
|
|
8927
9071
|
}
|
|
8928
9072
|
}
|
|
8929
9073
|
function parseRuleFile(filePath) {
|
|
8930
9074
|
try {
|
|
8931
|
-
const raw =
|
|
8932
|
-
const id =
|
|
9075
|
+
const raw = fs19.readFileSync(filePath, "utf-8");
|
|
9076
|
+
const id = path22.basename(filePath, ".md");
|
|
8933
9077
|
const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
8934
9078
|
if (!fm) {
|
|
8935
9079
|
return { id, description: id, paths: ["*"], content: raw.trim(), filePath };
|
|
@@ -8937,15 +9081,15 @@ function parseRuleFile(filePath) {
|
|
|
8937
9081
|
const meta = fm[1];
|
|
8938
9082
|
const body = fm[2].trim();
|
|
8939
9083
|
const description = meta.match(/description:\s*(.+)/)?.[1]?.trim() || id;
|
|
8940
|
-
const
|
|
9084
|
+
const paths2 = [];
|
|
8941
9085
|
const pathSection = meta.match(/paths:\s*\r?\n([\s\S]*?)(?=\r?\n\S|$)/);
|
|
8942
9086
|
if (pathSection) {
|
|
8943
9087
|
for (const line of pathSection[1].split("\n")) {
|
|
8944
9088
|
const m = line.match(/^\s*-\s*['"]?([^'"]+)['"]?\s*$/);
|
|
8945
|
-
if (m)
|
|
9089
|
+
if (m) paths2.push(m[1].trim());
|
|
8946
9090
|
}
|
|
8947
9091
|
}
|
|
8948
|
-
return { id, description, paths:
|
|
9092
|
+
return { id, description, paths: paths2.length ? paths2 : ["*"], content: body, filePath };
|
|
8949
9093
|
} catch {
|
|
8950
9094
|
return null;
|
|
8951
9095
|
}
|
|
@@ -8954,7 +9098,7 @@ function loadPathRules() {
|
|
|
8954
9098
|
ensureRulesDir();
|
|
8955
9099
|
try {
|
|
8956
9100
|
const dir = rulesDir();
|
|
8957
|
-
return
|
|
9101
|
+
return fs19.readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => parseRuleFile(path22.join(dir, f))).filter((r) => r !== null);
|
|
8958
9102
|
} catch {
|
|
8959
9103
|
return [];
|
|
8960
9104
|
}
|
|
@@ -9042,8 +9186,8 @@ __export(kumaFlowCache_exports, {
|
|
|
9042
9186
|
getFreshDomainFlow: () => getFreshDomainFlow,
|
|
9043
9187
|
getHopsForDomain: () => getHopsForDomain
|
|
9044
9188
|
});
|
|
9045
|
-
import
|
|
9046
|
-
import
|
|
9189
|
+
import fs20 from "fs";
|
|
9190
|
+
import path23 from "path";
|
|
9047
9191
|
async function getFlowFreshness(domain) {
|
|
9048
9192
|
try {
|
|
9049
9193
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
@@ -9078,26 +9222,26 @@ async function getFlowFreshness(domain) {
|
|
|
9078
9222
|
}
|
|
9079
9223
|
function resolveImport(fromFile, spec) {
|
|
9080
9224
|
const root = getProjectRoot();
|
|
9081
|
-
const fromDir =
|
|
9225
|
+
const fromDir = path23.dirname(fromFile);
|
|
9082
9226
|
const candidates = [];
|
|
9083
9227
|
if (spec.startsWith(".")) {
|
|
9084
|
-
const abs =
|
|
9228
|
+
const abs = path23.resolve(fromDir, spec);
|
|
9085
9229
|
candidates.push(abs);
|
|
9086
9230
|
} else {
|
|
9087
|
-
candidates.push(
|
|
9231
|
+
candidates.push(path23.join(root, spec));
|
|
9088
9232
|
}
|
|
9089
9233
|
for (const c of candidates) {
|
|
9090
9234
|
for (const ext of SOURCE_EXTENSIONS) {
|
|
9091
9235
|
const withExt = `${c}${ext}`;
|
|
9092
|
-
if (
|
|
9093
|
-
if (
|
|
9094
|
-
const idx =
|
|
9095
|
-
if (
|
|
9236
|
+
if (fs20.existsSync(withExt)) return path23.relative(root, withExt);
|
|
9237
|
+
if (fs20.existsSync(c) && fs20.statSync(c).isDirectory()) {
|
|
9238
|
+
const idx = path23.join(c, "index") + ext;
|
|
9239
|
+
if (fs20.existsSync(idx)) return path23.relative(root, idx);
|
|
9096
9240
|
}
|
|
9097
9241
|
}
|
|
9098
|
-
if (
|
|
9242
|
+
if (fs20.existsSync(c)) {
|
|
9099
9243
|
try {
|
|
9100
|
-
if (
|
|
9244
|
+
if (fs20.statSync(c).isFile()) return path23.relative(root, c);
|
|
9101
9245
|
} catch {
|
|
9102
9246
|
}
|
|
9103
9247
|
}
|
|
@@ -9106,13 +9250,13 @@ function resolveImport(fromFile, spec) {
|
|
|
9106
9250
|
}
|
|
9107
9251
|
function deriveHopsFromImports(entryFile, maxHops = 6) {
|
|
9108
9252
|
const root = getProjectRoot();
|
|
9109
|
-
const absEntry =
|
|
9110
|
-
if (!
|
|
9253
|
+
const absEntry = path23.isAbsolute(entryFile) ? entryFile : path23.resolve(root, entryFile);
|
|
9254
|
+
if (!fs20.existsSync(absEntry)) return [];
|
|
9111
9255
|
const hops = [];
|
|
9112
9256
|
const seen = /* @__PURE__ */ new Set([entryFile]);
|
|
9113
|
-
const fromName =
|
|
9257
|
+
const fromName = path23.basename(absEntry);
|
|
9114
9258
|
try {
|
|
9115
|
-
const lines =
|
|
9259
|
+
const lines = fs20.readFileSync(absEntry, "utf-8").split("\n");
|
|
9116
9260
|
for (const line of lines) {
|
|
9117
9261
|
if (hops.length >= maxHops) break;
|
|
9118
9262
|
const spec = matchImportPath(line);
|
|
@@ -9122,9 +9266,9 @@ function deriveHopsFromImports(entryFile, maxHops = 6) {
|
|
|
9122
9266
|
seen.add(resolved);
|
|
9123
9267
|
hops.push({
|
|
9124
9268
|
from: fromName,
|
|
9125
|
-
to:
|
|
9269
|
+
to: path23.basename(resolved),
|
|
9126
9270
|
relation: "imports",
|
|
9127
|
-
description: `${fromName} imports ${
|
|
9271
|
+
description: `${fromName} imports ${path23.basename(resolved)}`
|
|
9128
9272
|
});
|
|
9129
9273
|
}
|
|
9130
9274
|
} catch {
|
|
@@ -9187,9 +9331,9 @@ async function getHopsForDomain(domain) {
|
|
|
9187
9331
|
}
|
|
9188
9332
|
try {
|
|
9189
9333
|
const root = getProjectRoot();
|
|
9190
|
-
const archPath =
|
|
9191
|
-
if (
|
|
9192
|
-
const content =
|
|
9334
|
+
const archPath = path23.join(root, ".kuma", "ARCHITECTURE_FLOW.md");
|
|
9335
|
+
if (fs20.existsSync(archPath)) {
|
|
9336
|
+
const content = fs20.readFileSync(archPath, "utf-8");
|
|
9193
9337
|
const lines = content.split("\n");
|
|
9194
9338
|
for (const line of lines) {
|
|
9195
9339
|
const domMatch = line.match(new RegExp(`domain:\\s*${escapeRegex(domain)}\\s*\\|\\s*hops:\\s*(.+)`, "i"));
|
|
@@ -9234,7 +9378,7 @@ Record one with kuma_memory({ action: 'arch_flow', content: 'domain: ${domain} |
|
|
|
9234
9378
|
hops: derivedHops,
|
|
9235
9379
|
filePaths: freshness.filePaths
|
|
9236
9380
|
});
|
|
9237
|
-
lines.push(`\u2705 Re-derived ${derivedHops.length} hop(s) from ${
|
|
9381
|
+
lines.push(`\u2705 Re-derived ${derivedHops.length} hop(s) from ${path23.basename(entry)} \u2014 cache refreshed.`);
|
|
9238
9382
|
hops.splice(0, hops.length, ...derivedHops);
|
|
9239
9383
|
} else {
|
|
9240
9384
|
lines.push("\u26A0\uFE0F No imports to follow \u2014 cache left as-is (record hops manually if needed).");
|
|
@@ -9287,10 +9431,10 @@ __export(packageBriefs_exports, {
|
|
|
9287
9431
|
readBrief: () => readBrief,
|
|
9288
9432
|
refreshPackageBriefs: () => refreshPackageBriefs
|
|
9289
9433
|
});
|
|
9290
|
-
import
|
|
9291
|
-
import
|
|
9434
|
+
import fs21 from "fs";
|
|
9435
|
+
import path24 from "path";
|
|
9292
9436
|
function briefsDir(root) {
|
|
9293
|
-
return
|
|
9437
|
+
return path24.join(root || getProjectRoot(), BRIEFS_DIR);
|
|
9294
9438
|
}
|
|
9295
9439
|
function slug(name) {
|
|
9296
9440
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 60) || "root";
|
|
@@ -9354,13 +9498,13 @@ async function refreshPackageBriefs() {
|
|
|
9354
9498
|
const dir = briefsDir(root);
|
|
9355
9499
|
try {
|
|
9356
9500
|
const ws = await getWorkspaceInfo(root);
|
|
9357
|
-
if (!
|
|
9501
|
+
if (!fs21.existsSync(dir)) fs21.mkdirSync(dir, { recursive: true });
|
|
9358
9502
|
let count = 0;
|
|
9359
9503
|
const pkgs = ws.isWorkspace ? ws.packages : [{ name: "root", path: ".", isRoot: true }];
|
|
9360
9504
|
for (const p of pkgs.slice(0, 20)) {
|
|
9361
9505
|
const pkg = p;
|
|
9362
9506
|
const content = await buildPackageBrief(pkg.name, pkg.path, root);
|
|
9363
|
-
|
|
9507
|
+
fs21.writeFileSync(path24.join(dir, `${slug(pkg.name)}.md`), content, "utf-8");
|
|
9364
9508
|
count++;
|
|
9365
9509
|
}
|
|
9366
9510
|
return { count, dir };
|
|
@@ -9370,9 +9514,9 @@ async function refreshPackageBriefs() {
|
|
|
9370
9514
|
}
|
|
9371
9515
|
function briefFreshness(pkgName) {
|
|
9372
9516
|
try {
|
|
9373
|
-
const fp =
|
|
9374
|
-
if (!
|
|
9375
|
-
const ageMs = Date.now() -
|
|
9517
|
+
const fp = path24.join(briefsDir(), `${slug(pkgName)}.md`);
|
|
9518
|
+
if (!fs21.existsSync(fp)) return { exists: false, fresh: false, age: "missing" };
|
|
9519
|
+
const ageMs = Date.now() - fs21.statSync(fp).mtimeMs;
|
|
9376
9520
|
return { exists: true, fresh: !isStale(Date.now() - ageMs, MAP_TTL_MS), age: formatAge(Date.now() - ageMs) };
|
|
9377
9521
|
} catch {
|
|
9378
9522
|
return { exists: false, fresh: false, age: "missing" };
|
|
@@ -9380,9 +9524,9 @@ function briefFreshness(pkgName) {
|
|
|
9380
9524
|
}
|
|
9381
9525
|
function readBrief(pkgName, maxChars = 1200) {
|
|
9382
9526
|
try {
|
|
9383
|
-
const fp =
|
|
9384
|
-
if (!
|
|
9385
|
-
const content =
|
|
9527
|
+
const fp = path24.join(briefsDir(), `${slug(pkgName)}.md`);
|
|
9528
|
+
if (!fs21.existsSync(fp)) return null;
|
|
9529
|
+
const content = fs21.readFileSync(fp, "utf-8");
|
|
9386
9530
|
return content.length > maxChars ? content.slice(0, maxChars) + "\n\u2026(truncated)" : content;
|
|
9387
9531
|
} catch {
|
|
9388
9532
|
return null;
|
|
@@ -9411,8 +9555,8 @@ __export(kumaSearch_exports, {
|
|
|
9411
9555
|
formatHybridResults: () => formatHybridResults,
|
|
9412
9556
|
hybridSearch: () => hybridSearch
|
|
9413
9557
|
});
|
|
9414
|
-
import
|
|
9415
|
-
import
|
|
9558
|
+
import fs23 from "fs";
|
|
9559
|
+
import path26 from "path";
|
|
9416
9560
|
function expandQueryTerms(query) {
|
|
9417
9561
|
const terms = query.toLowerCase().split(/[\s_-]+/).filter((t) => t.length > 2);
|
|
9418
9562
|
const expanded = /* @__PURE__ */ new Map();
|
|
@@ -9475,13 +9619,13 @@ async function buildSearchVectors() {
|
|
|
9475
9619
|
}
|
|
9476
9620
|
try {
|
|
9477
9621
|
const root = getProjectRoot();
|
|
9478
|
-
const memDir =
|
|
9479
|
-
if (
|
|
9480
|
-
const files =
|
|
9622
|
+
const memDir = path26.join(root, ".kuma", "memories");
|
|
9623
|
+
if (fs23.existsSync(memDir)) {
|
|
9624
|
+
const files = fs23.readdirSync(memDir).filter((f) => f.endsWith(".md")).slice(0, SCALE_LIMITS.maxMemoryFiles);
|
|
9481
9625
|
for (const file of files) {
|
|
9482
9626
|
try {
|
|
9483
|
-
const fullPath =
|
|
9484
|
-
const content =
|
|
9627
|
+
const fullPath = path26.join(memDir, file);
|
|
9628
|
+
const content = fs23.readFileSync(fullPath, "utf-8");
|
|
9485
9629
|
const text = content.toLowerCase();
|
|
9486
9630
|
const rawTerms = extractTerms(text);
|
|
9487
9631
|
const termCounts = /* @__PURE__ */ new Map();
|
|
@@ -9492,7 +9636,7 @@ async function buildSearchVectors() {
|
|
|
9492
9636
|
terms: termCounts,
|
|
9493
9637
|
source: file.replace(/\.md$/, ""),
|
|
9494
9638
|
sourceType: "memory",
|
|
9495
|
-
recency: recencyScore(Math.floor(
|
|
9639
|
+
recency: recencyScore(Math.floor(fs23.statSync(fullPath).mtimeMs / 1e3))
|
|
9496
9640
|
});
|
|
9497
9641
|
} catch {
|
|
9498
9642
|
}
|
|
@@ -9809,14 +9953,14 @@ __export(architectureGuard_exports, {
|
|
|
9809
9953
|
detectWorkspaceCircularDeps: () => detectWorkspaceCircularDeps,
|
|
9810
9954
|
loadArchitectureRules: () => loadArchitectureRules
|
|
9811
9955
|
});
|
|
9812
|
-
import
|
|
9813
|
-
import
|
|
9956
|
+
import fs27 from "fs";
|
|
9957
|
+
import path30 from "path";
|
|
9814
9958
|
import fastGlob4 from "fast-glob";
|
|
9815
9959
|
function loadArchitectureRules(root) {
|
|
9816
|
-
const rulesPath =
|
|
9817
|
-
if (
|
|
9960
|
+
const rulesPath = path30.join(root, ".kuma", "architecture.json");
|
|
9961
|
+
if (fs27.existsSync(rulesPath)) {
|
|
9818
9962
|
try {
|
|
9819
|
-
const data = JSON.parse(
|
|
9963
|
+
const data = JSON.parse(fs27.readFileSync(rulesPath, "utf-8"));
|
|
9820
9964
|
if (Array.isArray(data.rules)) {
|
|
9821
9965
|
return data.rules;
|
|
9822
9966
|
}
|
|
@@ -9878,11 +10022,11 @@ async function checkArchitectureBoundaries(filesToCheck, customRoot) {
|
|
|
9878
10022
|
const customRules = loadArchitectureRules(root);
|
|
9879
10023
|
const importRegex = /(?:import|export\s+.*from|require\s*\()\s*['"]([^'"]+)['"]/g;
|
|
9880
10024
|
for (const file of targetFiles) {
|
|
9881
|
-
const fullPath =
|
|
9882
|
-
if (!
|
|
10025
|
+
const fullPath = path30.isAbsolute(file) ? file : path30.join(root, file);
|
|
10026
|
+
if (!fs27.existsSync(fullPath)) continue;
|
|
9883
10027
|
let content = "";
|
|
9884
10028
|
try {
|
|
9885
|
-
content =
|
|
10029
|
+
content = fs27.readFileSync(fullPath, "utf-8");
|
|
9886
10030
|
} catch {
|
|
9887
10031
|
continue;
|
|
9888
10032
|
}
|
|
@@ -9892,7 +10036,7 @@ async function checkArchitectureBoundaries(filesToCheck, customRoot) {
|
|
|
9892
10036
|
const importPath = match[1];
|
|
9893
10037
|
if (wsInfo.isWorkspace && currentPkg && !currentPkg.isRoot) {
|
|
9894
10038
|
if (importPath.startsWith("../") && (importPath.includes("/src/") || importPath.includes("/dist/"))) {
|
|
9895
|
-
const resolvedTarget =
|
|
10039
|
+
const resolvedTarget = path30.normalize(path30.resolve(path30.dirname(fullPath), importPath));
|
|
9896
10040
|
const targetPkg = findPackageForFile(resolvedTarget, wsInfo);
|
|
9897
10041
|
if (targetPkg && targetPkg.name !== currentPkg.name) {
|
|
9898
10042
|
violations.push({
|
|
@@ -9941,26 +10085,26 @@ __export(docDrift_exports, {
|
|
|
9941
10085
|
formatDocDrift: () => formatDocDrift,
|
|
9942
10086
|
scanDocDrift: () => scanDocDrift
|
|
9943
10087
|
});
|
|
9944
|
-
import
|
|
9945
|
-
import
|
|
9946
|
-
import { execSync as
|
|
10088
|
+
import fs28 from "fs";
|
|
10089
|
+
import path31 from "path";
|
|
10090
|
+
import { execSync as execSync9 } from "child_process";
|
|
9947
10091
|
function collectDocFiles(root) {
|
|
9948
10092
|
const cwd = root || getProjectRoot();
|
|
9949
10093
|
const out = [];
|
|
9950
10094
|
const pushIfMd = (dir, rel) => {
|
|
9951
10095
|
try {
|
|
9952
|
-
for (const f of
|
|
10096
|
+
for (const f of fs28.readdirSync(dir)) {
|
|
9953
10097
|
if (out.length >= MAX_DOCS) return;
|
|
9954
|
-
const full =
|
|
10098
|
+
const full = path31.join(dir, f);
|
|
9955
10099
|
try {
|
|
9956
|
-
if (
|
|
10100
|
+
if (fs28.statSync(full).isFile() && /\.md$/i.test(f)) out.push(rel(f));
|
|
9957
10101
|
} catch {
|
|
9958
10102
|
}
|
|
9959
10103
|
}
|
|
9960
10104
|
} catch {
|
|
9961
10105
|
}
|
|
9962
10106
|
};
|
|
9963
|
-
pushIfMd(
|
|
10107
|
+
pushIfMd(path31.join(cwd, "docs"), (f) => path31.join("docs", f));
|
|
9964
10108
|
pushIfMd(cwd, (f) => f);
|
|
9965
10109
|
return out;
|
|
9966
10110
|
}
|
|
@@ -10002,7 +10146,7 @@ function codeContainsBatch(root, needles) {
|
|
|
10002
10146
|
const chunk = clean.slice(i, i + 50);
|
|
10003
10147
|
try {
|
|
10004
10148
|
const args = chunk.map((n) => `-e "${n}"`).join(" ");
|
|
10005
|
-
const out =
|
|
10149
|
+
const out = execSync9(
|
|
10006
10150
|
`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`,
|
|
10007
10151
|
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
10008
10152
|
).trim();
|
|
@@ -10022,7 +10166,7 @@ function scanDocDrift(root) {
|
|
|
10022
10166
|
let claims = [];
|
|
10023
10167
|
for (const rel of collectDocFiles(cwd)) {
|
|
10024
10168
|
try {
|
|
10025
|
-
const content =
|
|
10169
|
+
const content = fs28.readFileSync(path31.join(cwd, rel), "utf-8");
|
|
10026
10170
|
if (isExemptDoc(rel, content)) {
|
|
10027
10171
|
skipped.push(rel);
|
|
10028
10172
|
continue;
|
|
@@ -10193,27 +10337,27 @@ __export(kumaCheckpoint_exports, {
|
|
|
10193
10337
|
pruneCheckpoints: () => pruneCheckpoints,
|
|
10194
10338
|
rollbackToCheckpoint: () => rollbackToCheckpoint
|
|
10195
10339
|
});
|
|
10196
|
-
import
|
|
10197
|
-
import
|
|
10340
|
+
import fs29 from "fs";
|
|
10341
|
+
import path32 from "path";
|
|
10198
10342
|
async function createCheckpoint(label, description) {
|
|
10199
10343
|
try {
|
|
10200
10344
|
const root = getProjectRoot();
|
|
10201
|
-
const cpDir =
|
|
10202
|
-
if (
|
|
10345
|
+
const cpDir = path32.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
10346
|
+
if (fs29.existsSync(cpDir)) {
|
|
10203
10347
|
return `\u26A0\uFE0F Checkpoint "${label}" already exists. Use a different label or remove it first.`;
|
|
10204
10348
|
}
|
|
10205
|
-
|
|
10349
|
+
fs29.mkdirSync(cpDir, { recursive: true });
|
|
10206
10350
|
let gitHead;
|
|
10207
10351
|
let isGitClean = false;
|
|
10208
10352
|
try {
|
|
10209
|
-
const { execSync:
|
|
10210
|
-
gitHead =
|
|
10353
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10354
|
+
gitHead = execSync10("git rev-parse HEAD", {
|
|
10211
10355
|
cwd: root,
|
|
10212
10356
|
encoding: "utf-8",
|
|
10213
10357
|
timeout: 3e3,
|
|
10214
10358
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10215
10359
|
}).trim();
|
|
10216
|
-
const statusOut =
|
|
10360
|
+
const statusOut = execSync10("git status --porcelain", {
|
|
10217
10361
|
cwd: root,
|
|
10218
10362
|
encoding: "utf-8",
|
|
10219
10363
|
timeout: 3e3,
|
|
@@ -10224,8 +10368,8 @@ async function createCheckpoint(label, description) {
|
|
|
10224
10368
|
}
|
|
10225
10369
|
const candidatePaths = /* @__PURE__ */ new Set();
|
|
10226
10370
|
try {
|
|
10227
|
-
const { execSync:
|
|
10228
|
-
const gitOut =
|
|
10371
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10372
|
+
const gitOut = execSync10("git status --porcelain", {
|
|
10229
10373
|
cwd: root,
|
|
10230
10374
|
encoding: "utf-8",
|
|
10231
10375
|
timeout: 4e3,
|
|
@@ -10268,15 +10412,15 @@ async function createCheckpoint(label, description) {
|
|
|
10268
10412
|
}
|
|
10269
10413
|
const files = [];
|
|
10270
10414
|
for (const fp of candidatePaths) {
|
|
10271
|
-
const fullPath =
|
|
10415
|
+
const fullPath = path32.resolve(root, fp);
|
|
10272
10416
|
try {
|
|
10273
|
-
if (
|
|
10274
|
-
const content =
|
|
10417
|
+
if (fs29.existsSync(fullPath) && fs29.statSync(fullPath).isFile()) {
|
|
10418
|
+
const content = fs29.readFileSync(fullPath, "utf-8");
|
|
10275
10419
|
const hash = simpleHash2(content);
|
|
10276
|
-
const snapFile =
|
|
10277
|
-
const fileDir =
|
|
10278
|
-
if (!
|
|
10279
|
-
|
|
10420
|
+
const snapFile = path32.join(cpDir, "files", `${fp}.kuma-snap`);
|
|
10421
|
+
const fileDir = path32.dirname(snapFile);
|
|
10422
|
+
if (!fs29.existsSync(fileDir)) fs29.mkdirSync(fileDir, { recursive: true });
|
|
10423
|
+
fs29.writeFileSync(snapFile, content, "utf-8");
|
|
10280
10424
|
files.push({ path: fp, hash });
|
|
10281
10425
|
}
|
|
10282
10426
|
} catch {
|
|
@@ -10284,7 +10428,7 @@ async function createCheckpoint(label, description) {
|
|
|
10284
10428
|
}
|
|
10285
10429
|
const db = await getDb();
|
|
10286
10430
|
const dbData = db.export();
|
|
10287
|
-
|
|
10431
|
+
fs29.writeFileSync(path32.join(cpDir, "kuma.db"), Buffer.from(dbData));
|
|
10288
10432
|
const manifest = {
|
|
10289
10433
|
label,
|
|
10290
10434
|
timestamp: Date.now(),
|
|
@@ -10294,8 +10438,8 @@ async function createCheckpoint(label, description) {
|
|
|
10294
10438
|
dbSnapshot: true,
|
|
10295
10439
|
description
|
|
10296
10440
|
};
|
|
10297
|
-
|
|
10298
|
-
|
|
10441
|
+
fs29.writeFileSync(
|
|
10442
|
+
path32.join(cpDir, "manifest.json"),
|
|
10299
10443
|
JSON.stringify(manifest, null, 2),
|
|
10300
10444
|
"utf-8"
|
|
10301
10445
|
);
|
|
@@ -10323,29 +10467,29 @@ async function createCheckpoint(label, description) {
|
|
|
10323
10467
|
async function rollbackToCheckpoint(label) {
|
|
10324
10468
|
try {
|
|
10325
10469
|
const root = getProjectRoot();
|
|
10326
|
-
const cpDir =
|
|
10327
|
-
const manifestPath =
|
|
10328
|
-
if (!
|
|
10470
|
+
const cpDir = path32.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
10471
|
+
const manifestPath = path32.join(cpDir, "manifest.json");
|
|
10472
|
+
if (!fs29.existsSync(manifestPath)) {
|
|
10329
10473
|
return `\u274C Checkpoint "${label}" not found. Available checkpoints:
|
|
10330
10474
|
|
|
10331
10475
|
${listCheckpoints()}`;
|
|
10332
10476
|
}
|
|
10333
10477
|
const manifest = JSON.parse(
|
|
10334
|
-
|
|
10478
|
+
fs29.readFileSync(manifestPath, "utf-8")
|
|
10335
10479
|
);
|
|
10336
10480
|
let restored = 0;
|
|
10337
10481
|
let failed = 0;
|
|
10338
10482
|
for (const f of manifest.files) {
|
|
10339
|
-
const snapPath =
|
|
10340
|
-
const legacyPath =
|
|
10341
|
-
const sourcePath =
|
|
10342
|
-
if (
|
|
10483
|
+
const snapPath = path32.join(cpDir, "files", `${f.path}.kuma-snap`);
|
|
10484
|
+
const legacyPath = path32.join(cpDir, "files", f.path);
|
|
10485
|
+
const sourcePath = fs29.existsSync(snapPath) ? snapPath : legacyPath;
|
|
10486
|
+
if (fs29.existsSync(sourcePath)) {
|
|
10343
10487
|
try {
|
|
10344
|
-
const content =
|
|
10345
|
-
const targetPath =
|
|
10346
|
-
const targetDir =
|
|
10347
|
-
if (!
|
|
10348
|
-
|
|
10488
|
+
const content = fs29.readFileSync(sourcePath, "utf-8");
|
|
10489
|
+
const targetPath = path32.resolve(root, f.path);
|
|
10490
|
+
const targetDir = path32.dirname(targetPath);
|
|
10491
|
+
if (!fs29.existsSync(targetDir)) fs29.mkdirSync(targetDir, { recursive: true });
|
|
10492
|
+
fs29.writeFileSync(targetPath, content, "utf-8");
|
|
10349
10493
|
restored++;
|
|
10350
10494
|
} catch {
|
|
10351
10495
|
failed++;
|
|
@@ -10354,8 +10498,8 @@ ${listCheckpoints()}`;
|
|
|
10354
10498
|
}
|
|
10355
10499
|
if (manifest.files.length === 0 && manifest.gitHead) {
|
|
10356
10500
|
try {
|
|
10357
|
-
const { execSync:
|
|
10358
|
-
|
|
10501
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10502
|
+
execSync10(`git checkout ${manifest.gitHead} -- .`, {
|
|
10359
10503
|
cwd: root,
|
|
10360
10504
|
encoding: "utf-8",
|
|
10361
10505
|
timeout: 8e3,
|
|
@@ -10366,12 +10510,12 @@ ${listCheckpoints()}`;
|
|
|
10366
10510
|
failed++;
|
|
10367
10511
|
}
|
|
10368
10512
|
}
|
|
10369
|
-
const dbSnapshotPath =
|
|
10370
|
-
if (
|
|
10371
|
-
const kumaDir =
|
|
10372
|
-
const dbPath =
|
|
10373
|
-
const snapshotData =
|
|
10374
|
-
|
|
10513
|
+
const dbSnapshotPath = path32.join(cpDir, "kuma.db");
|
|
10514
|
+
if (fs29.existsSync(dbSnapshotPath)) {
|
|
10515
|
+
const kumaDir = path32.join(root, ".kuma");
|
|
10516
|
+
const dbPath = path32.join(kumaDir, "kuma.db");
|
|
10517
|
+
const snapshotData = fs29.readFileSync(dbSnapshotPath);
|
|
10518
|
+
fs29.writeFileSync(dbPath, snapshotData);
|
|
10375
10519
|
resetDbInstance();
|
|
10376
10520
|
}
|
|
10377
10521
|
sessionMemory.recordToolCall("kuma_checkpoint_rollback", {
|
|
@@ -10396,18 +10540,18 @@ ${listCheckpoints()}`;
|
|
|
10396
10540
|
function listCheckpoints() {
|
|
10397
10541
|
try {
|
|
10398
10542
|
const root = getProjectRoot();
|
|
10399
|
-
const cpDir =
|
|
10400
|
-
if (!
|
|
10543
|
+
const cpDir = path32.join(root, CHECKPOINT_DIR);
|
|
10544
|
+
if (!fs29.existsSync(cpDir)) {
|
|
10401
10545
|
return "\u{1F4ED} No checkpoints found. Use kuma_safety({ action: 'checkpoint', label: 'pre-feature-x' }) to create one.";
|
|
10402
10546
|
}
|
|
10403
|
-
const entries =
|
|
10547
|
+
const entries = fs29.readdirSync(cpDir);
|
|
10404
10548
|
const checkpoints = [];
|
|
10405
10549
|
for (const entry of entries) {
|
|
10406
|
-
const manifestPath =
|
|
10407
|
-
if (
|
|
10550
|
+
const manifestPath = path32.join(cpDir, entry, "manifest.json");
|
|
10551
|
+
if (fs29.existsSync(manifestPath)) {
|
|
10408
10552
|
try {
|
|
10409
10553
|
const manifest = JSON.parse(
|
|
10410
|
-
|
|
10554
|
+
fs29.readFileSync(manifestPath, "utf-8")
|
|
10411
10555
|
);
|
|
10412
10556
|
checkpoints.push({ label: entry, manifest });
|
|
10413
10557
|
} catch {
|
|
@@ -10439,12 +10583,12 @@ function listCheckpoints() {
|
|
|
10439
10583
|
function pruneCheckpoints(keep = 5) {
|
|
10440
10584
|
try {
|
|
10441
10585
|
const root = getProjectRoot();
|
|
10442
|
-
const cpDir =
|
|
10443
|
-
if (!
|
|
10444
|
-
const entries =
|
|
10586
|
+
const cpDir = path32.join(root, CHECKPOINT_DIR);
|
|
10587
|
+
if (!fs29.existsSync(cpDir)) return "\u{1F4ED} No checkpoints to prune.";
|
|
10588
|
+
const entries = fs29.readdirSync(cpDir).map((e) => ({ name: e, time: fs29.statSync(path32.join(cpDir, e)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
10445
10589
|
let removed = 0;
|
|
10446
10590
|
for (let i = keep; i < entries.length; i++) {
|
|
10447
|
-
|
|
10591
|
+
fs29.rmSync(path32.join(cpDir, entries[i].name), { recursive: true, force: true });
|
|
10448
10592
|
removed++;
|
|
10449
10593
|
}
|
|
10450
10594
|
return removed > 0 ? `\u{1F9F9} Pruned **${removed} old checkpoint(s)**. Kept ${Math.min(keep, entries.length)} most recent.` : "\u2705 No checkpoints needed pruning.";
|
|
@@ -10469,9 +10613,9 @@ function isRiskyEditPath(filePath) {
|
|
|
10469
10613
|
}
|
|
10470
10614
|
function loadAutoCpDone(root) {
|
|
10471
10615
|
try {
|
|
10472
|
-
const fp =
|
|
10473
|
-
if (
|
|
10474
|
-
const parsed = JSON.parse(
|
|
10616
|
+
const fp = path32.join(root, AUTO_CP_STATE);
|
|
10617
|
+
if (fs29.existsSync(fp)) {
|
|
10618
|
+
const parsed = JSON.parse(fs29.readFileSync(fp, "utf-8"));
|
|
10475
10619
|
return parsed?.autoCheckpoints ?? {};
|
|
10476
10620
|
}
|
|
10477
10621
|
} catch {
|
|
@@ -10480,10 +10624,10 @@ function loadAutoCpDone(root) {
|
|
|
10480
10624
|
}
|
|
10481
10625
|
function saveAutoCpDone(root, done) {
|
|
10482
10626
|
try {
|
|
10483
|
-
const fp =
|
|
10484
|
-
const parsed =
|
|
10627
|
+
const fp = path32.join(root, AUTO_CP_STATE);
|
|
10628
|
+
const parsed = fs29.existsSync(fp) ? JSON.parse(fs29.readFileSync(fp, "utf-8")) : {};
|
|
10485
10629
|
parsed.autoCheckpoints = done;
|
|
10486
|
-
|
|
10630
|
+
fs29.writeFileSync(fp, JSON.stringify(parsed), "utf-8");
|
|
10487
10631
|
} catch {
|
|
10488
10632
|
}
|
|
10489
10633
|
}
|
|
@@ -10493,14 +10637,14 @@ function maybeAutoCheckpoint(filePath) {
|
|
|
10493
10637
|
const root = getProjectRoot();
|
|
10494
10638
|
const done = loadAutoCpDone(root);
|
|
10495
10639
|
if (done[filePath]) return null;
|
|
10496
|
-
const abs =
|
|
10497
|
-
if (!
|
|
10498
|
-
const label = `auto-pre-${sanitizeLabel(
|
|
10499
|
-
const cpDir =
|
|
10500
|
-
if (!
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10640
|
+
const abs = path32.isAbsolute(filePath) ? filePath : path32.join(root, filePath);
|
|
10641
|
+
if (!fs29.existsSync(abs) || !fs29.statSync(abs).isFile()) return null;
|
|
10642
|
+
const label = `auto-pre-${sanitizeLabel(path32.basename(filePath))}`;
|
|
10643
|
+
const cpDir = path32.join(root, CHECKPOINT_DIR, label);
|
|
10644
|
+
if (!fs29.existsSync(cpDir)) fs29.mkdirSync(cpDir, { recursive: true });
|
|
10645
|
+
fs29.copyFileSync(abs, path32.join(cpDir, path32.basename(abs)));
|
|
10646
|
+
fs29.writeFileSync(
|
|
10647
|
+
path32.join(cpDir, "manifest.json"),
|
|
10504
10648
|
JSON.stringify({
|
|
10505
10649
|
label,
|
|
10506
10650
|
timestamp: Date.now(),
|
|
@@ -10540,8 +10684,8 @@ __export(init_exports, {
|
|
|
10540
10684
|
generateInitMdContent: () => generateInitMdContent,
|
|
10541
10685
|
runInit: () => runInit
|
|
10542
10686
|
});
|
|
10543
|
-
import
|
|
10544
|
-
import
|
|
10687
|
+
import fs30 from "fs";
|
|
10688
|
+
import path33 from "path";
|
|
10545
10689
|
function configFilePath(type) {
|
|
10546
10690
|
switch (type) {
|
|
10547
10691
|
case "claude":
|
|
@@ -11175,21 +11319,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11175
11319
|
"",
|
|
11176
11320
|
"\u{1F4D6} Full rules: `.kuma/init.md`"
|
|
11177
11321
|
].join("\n");
|
|
11178
|
-
const skillPath =
|
|
11322
|
+
const skillPath = path33.resolve(root, ".agents", "skills", "kuma", "SKILL.md");
|
|
11179
11323
|
if (!results.some((r) => r.filePath === ".agents/skills/kuma/SKILL.md")) {
|
|
11180
11324
|
try {
|
|
11181
|
-
const dir =
|
|
11182
|
-
if (
|
|
11183
|
-
const existing =
|
|
11325
|
+
const dir = path33.dirname(skillPath);
|
|
11326
|
+
if (fs30.existsSync(skillPath)) {
|
|
11327
|
+
const existing = fs30.readFileSync(skillPath, "utf-8");
|
|
11184
11328
|
if (existing.includes("kuma-mcp")) {
|
|
11185
11329
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "skipped" });
|
|
11186
11330
|
} else {
|
|
11187
|
-
|
|
11331
|
+
fs30.writeFileSync(skillPath, existing.trimEnd() + "\n\n---\n\n" + opencodeContent, "utf-8");
|
|
11188
11332
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "appended" });
|
|
11189
11333
|
}
|
|
11190
11334
|
} else {
|
|
11191
|
-
if (!
|
|
11192
|
-
|
|
11335
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11336
|
+
fs30.writeFileSync(skillPath, opencodeContent, "utf-8");
|
|
11193
11337
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "created" });
|
|
11194
11338
|
}
|
|
11195
11339
|
} catch (err) {
|
|
@@ -11201,21 +11345,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11201
11345
|
});
|
|
11202
11346
|
}
|
|
11203
11347
|
}
|
|
11204
|
-
const opencodeSkillPath =
|
|
11348
|
+
const opencodeSkillPath = path33.resolve(root, ".opencode", "skills", "kuma", "SKILL.md");
|
|
11205
11349
|
if (!results.some((r) => r.filePath === ".opencode/skills/kuma/SKILL.md")) {
|
|
11206
11350
|
try {
|
|
11207
|
-
const dir =
|
|
11208
|
-
if (
|
|
11209
|
-
const existing =
|
|
11351
|
+
const dir = path33.dirname(opencodeSkillPath);
|
|
11352
|
+
if (fs30.existsSync(opencodeSkillPath)) {
|
|
11353
|
+
const existing = fs30.readFileSync(opencodeSkillPath, "utf-8");
|
|
11210
11354
|
if (existing.includes("kuma-mcp")) {
|
|
11211
11355
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "skipped" });
|
|
11212
11356
|
} else {
|
|
11213
|
-
|
|
11357
|
+
fs30.writeFileSync(opencodeSkillPath, existing.trimEnd() + "\n\n---\n\n" + opencodeContent, "utf-8");
|
|
11214
11358
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "appended" });
|
|
11215
11359
|
}
|
|
11216
11360
|
} else {
|
|
11217
|
-
if (!
|
|
11218
|
-
|
|
11361
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11362
|
+
fs30.writeFileSync(opencodeSkillPath, opencodeContent, "utf-8");
|
|
11219
11363
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "created" });
|
|
11220
11364
|
}
|
|
11221
11365
|
} catch (err) {
|
|
@@ -11227,21 +11371,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11227
11371
|
});
|
|
11228
11372
|
}
|
|
11229
11373
|
}
|
|
11230
|
-
const pluginPath =
|
|
11374
|
+
const pluginPath = path33.resolve(root, ".opencode", "plugins", "kuma.js");
|
|
11231
11375
|
if (!results.some((r) => r.filePath === ".opencode/plugins/kuma.js")) {
|
|
11232
11376
|
try {
|
|
11233
|
-
const dir =
|
|
11234
|
-
if (
|
|
11235
|
-
const existing =
|
|
11377
|
+
const dir = path33.dirname(pluginPath);
|
|
11378
|
+
if (fs30.existsSync(pluginPath)) {
|
|
11379
|
+
const existing = fs30.readFileSync(pluginPath, "utf-8");
|
|
11236
11380
|
if (existing.includes("KumaPlugin") || existing.includes("kuma")) {
|
|
11237
11381
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "skipped" });
|
|
11238
11382
|
} else {
|
|
11239
|
-
|
|
11383
|
+
fs30.writeFileSync(pluginPath, existing.trimEnd() + "\n\n" + opencodePluginTemplate(), "utf-8");
|
|
11240
11384
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "appended" });
|
|
11241
11385
|
}
|
|
11242
11386
|
} else {
|
|
11243
|
-
if (!
|
|
11244
|
-
|
|
11387
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11388
|
+
fs30.writeFileSync(pluginPath, opencodePluginTemplate(), "utf-8");
|
|
11245
11389
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "created" });
|
|
11246
11390
|
}
|
|
11247
11391
|
} catch (err) {
|
|
@@ -11255,21 +11399,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11255
11399
|
}
|
|
11256
11400
|
}
|
|
11257
11401
|
function handleCodexSecondary(root, results) {
|
|
11258
|
-
const tomlPath =
|
|
11402
|
+
const tomlPath = path33.resolve(root, ".codex/config.toml");
|
|
11259
11403
|
if (results.some((r) => r.filePath === ".codex/config.toml")) return;
|
|
11260
11404
|
try {
|
|
11261
|
-
const dir =
|
|
11262
|
-
if (!
|
|
11263
|
-
if (
|
|
11264
|
-
const existingContent =
|
|
11405
|
+
const dir = path33.dirname(tomlPath);
|
|
11406
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11407
|
+
if (fs30.existsSync(tomlPath)) {
|
|
11408
|
+
const existingContent = fs30.readFileSync(tomlPath, "utf-8");
|
|
11265
11409
|
if (existingContent.includes("kuma")) {
|
|
11266
11410
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "skipped" });
|
|
11267
11411
|
return;
|
|
11268
11412
|
}
|
|
11269
|
-
|
|
11413
|
+
fs30.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
|
|
11270
11414
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "appended" });
|
|
11271
11415
|
} else {
|
|
11272
|
-
|
|
11416
|
+
fs30.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
|
|
11273
11417
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "created" });
|
|
11274
11418
|
}
|
|
11275
11419
|
} catch (err) {
|
|
@@ -11282,11 +11426,11 @@ function handleCodexSecondary(root, results) {
|
|
|
11282
11426
|
}
|
|
11283
11427
|
}
|
|
11284
11428
|
function handleQwenSecondary(root, results) {
|
|
11285
|
-
const settingsPath =
|
|
11429
|
+
const settingsPath = path33.resolve(root, "settings.json");
|
|
11286
11430
|
if (results.some((r) => r.filePath === "settings.json")) return;
|
|
11287
11431
|
try {
|
|
11288
|
-
if (
|
|
11289
|
-
const existingContent =
|
|
11432
|
+
if (fs30.existsSync(settingsPath)) {
|
|
11433
|
+
const existingContent = fs30.readFileSync(settingsPath, "utf-8");
|
|
11290
11434
|
if (existingContent.includes("kuma")) {
|
|
11291
11435
|
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
11292
11436
|
try {
|
|
@@ -11294,7 +11438,7 @@ function handleQwenSecondary(root, results) {
|
|
|
11294
11438
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11295
11439
|
if (!parsed.mcpServers.kuma) {
|
|
11296
11440
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11297
|
-
|
|
11441
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11298
11442
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
11299
11443
|
return;
|
|
11300
11444
|
}
|
|
@@ -11309,13 +11453,13 @@ function handleQwenSecondary(root, results) {
|
|
|
11309
11453
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11310
11454
|
if (!parsed.mcpServers.kuma) {
|
|
11311
11455
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11312
|
-
|
|
11456
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11313
11457
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
11314
11458
|
}
|
|
11315
11459
|
} catch {
|
|
11316
11460
|
}
|
|
11317
11461
|
} else {
|
|
11318
|
-
|
|
11462
|
+
fs30.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
|
|
11319
11463
|
results.push({ type: "qwen", filePath: "settings.json", action: "created" });
|
|
11320
11464
|
}
|
|
11321
11465
|
} catch (err) {
|
|
@@ -11417,12 +11561,12 @@ function getCombinedAgentsMd(selectedTypes) {
|
|
|
11417
11561
|
return lines.join("\n");
|
|
11418
11562
|
}
|
|
11419
11563
|
function handleAntigravityMcpConfig(root, results) {
|
|
11420
|
-
const mcpPath =
|
|
11564
|
+
const mcpPath = path33.resolve(root, ".agents/mcp_config.json");
|
|
11421
11565
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
11422
11566
|
try {
|
|
11423
|
-
const mcpDir =
|
|
11424
|
-
if (
|
|
11425
|
-
const existingContent =
|
|
11567
|
+
const mcpDir = path33.dirname(mcpPath);
|
|
11568
|
+
if (fs30.existsSync(mcpPath)) {
|
|
11569
|
+
const existingContent = fs30.readFileSync(mcpPath, "utf-8");
|
|
11426
11570
|
if (existingContent.includes("kuma")) {
|
|
11427
11571
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "skipped" });
|
|
11428
11572
|
return;
|
|
@@ -11431,14 +11575,14 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
11431
11575
|
const parsed = JSON.parse(existingContent);
|
|
11432
11576
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11433
11577
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11434
|
-
|
|
11578
|
+
fs30.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11435
11579
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
11436
11580
|
} catch {
|
|
11437
11581
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "error", error: "Invalid JSON in existing mcp_config.json" });
|
|
11438
11582
|
}
|
|
11439
11583
|
} else {
|
|
11440
|
-
if (!
|
|
11441
|
-
|
|
11584
|
+
if (!fs30.existsSync(mcpDir)) fs30.mkdirSync(mcpDir, { recursive: true });
|
|
11585
|
+
fs30.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
11442
11586
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "created" });
|
|
11443
11587
|
}
|
|
11444
11588
|
} catch (err) {
|
|
@@ -11451,21 +11595,21 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
11451
11595
|
}
|
|
11452
11596
|
}
|
|
11453
11597
|
function handleAntigravityRules(root, results) {
|
|
11454
|
-
const rulePath =
|
|
11598
|
+
const rulePath = path33.resolve(root, ".agents", "rules", "kuma.md");
|
|
11455
11599
|
if (results.some((r) => r.filePath === ".agents/rules/kuma.md")) return;
|
|
11456
11600
|
try {
|
|
11457
|
-
const dir =
|
|
11458
|
-
if (!
|
|
11459
|
-
if (
|
|
11460
|
-
const existing =
|
|
11601
|
+
const dir = path33.dirname(rulePath);
|
|
11602
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11603
|
+
if (fs30.existsSync(rulePath)) {
|
|
11604
|
+
const existing = fs30.readFileSync(rulePath, "utf-8");
|
|
11461
11605
|
if (existing.includes("Kuma MCP") || existing.includes("kuma-mcp")) {
|
|
11462
11606
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "skipped" });
|
|
11463
11607
|
return;
|
|
11464
11608
|
}
|
|
11465
|
-
|
|
11609
|
+
fs30.writeFileSync(rulePath, existing.trimEnd() + "\n\n---\n\n" + antigravityRuleTemplate(), "utf-8");
|
|
11466
11610
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "appended" });
|
|
11467
11611
|
} else {
|
|
11468
|
-
|
|
11612
|
+
fs30.writeFileSync(rulePath, antigravityRuleTemplate(), "utf-8");
|
|
11469
11613
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "created" });
|
|
11470
11614
|
}
|
|
11471
11615
|
} catch (err) {
|
|
@@ -11478,19 +11622,19 @@ function handleAntigravityRules(root, results) {
|
|
|
11478
11622
|
}
|
|
11479
11623
|
}
|
|
11480
11624
|
function handleGeminiMd(root, results) {
|
|
11481
|
-
const geminiPath =
|
|
11625
|
+
const geminiPath = path33.resolve(root, "GEMINI.md");
|
|
11482
11626
|
if (results.some((r) => r.filePath === "GEMINI.md")) return;
|
|
11483
11627
|
try {
|
|
11484
|
-
if (
|
|
11485
|
-
const existing =
|
|
11628
|
+
if (fs30.existsSync(geminiPath)) {
|
|
11629
|
+
const existing = fs30.readFileSync(geminiPath, "utf-8");
|
|
11486
11630
|
if (existing.includes("Kuma MCP") || existing.includes("kuma_context")) {
|
|
11487
11631
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "skipped" });
|
|
11488
11632
|
return;
|
|
11489
11633
|
}
|
|
11490
|
-
|
|
11634
|
+
fs30.writeFileSync(geminiPath, existing.trimEnd() + "\n\n---\n\n" + geminiTemplate(), "utf-8");
|
|
11491
11635
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "appended" });
|
|
11492
11636
|
} else {
|
|
11493
|
-
|
|
11637
|
+
fs30.writeFileSync(geminiPath, geminiTemplate(), "utf-8");
|
|
11494
11638
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "created" });
|
|
11495
11639
|
}
|
|
11496
11640
|
} catch (err) {
|
|
@@ -11508,19 +11652,19 @@ function handleAntigravitySecondary(root, results) {
|
|
|
11508
11652
|
handleGeminiMd(root, results);
|
|
11509
11653
|
}
|
|
11510
11654
|
function handleWindsurfSecondary(root, results) {
|
|
11511
|
-
const rulesPath =
|
|
11655
|
+
const rulesPath = path33.resolve(root, ".windsurfrules");
|
|
11512
11656
|
if (results.some((r) => r.filePath === ".windsurfrules")) return;
|
|
11513
11657
|
try {
|
|
11514
|
-
if (
|
|
11515
|
-
const existing =
|
|
11658
|
+
if (fs30.existsSync(rulesPath)) {
|
|
11659
|
+
const existing = fs30.readFileSync(rulesPath, "utf-8");
|
|
11516
11660
|
if (existing.includes("Kuma MCP") || existing.includes("kuma_context")) {
|
|
11517
11661
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "skipped" });
|
|
11518
11662
|
return;
|
|
11519
11663
|
}
|
|
11520
|
-
|
|
11664
|
+
fs30.writeFileSync(rulesPath, existing.trimEnd() + "\n\n---\n\n" + windsurfrulesTemplate(), "utf-8");
|
|
11521
11665
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "appended" });
|
|
11522
11666
|
} else {
|
|
11523
|
-
|
|
11667
|
+
fs30.writeFileSync(rulesPath, windsurfrulesTemplate(), "utf-8");
|
|
11524
11668
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "created" });
|
|
11525
11669
|
}
|
|
11526
11670
|
} catch (err) {
|
|
@@ -11533,21 +11677,21 @@ function handleWindsurfSecondary(root, results) {
|
|
|
11533
11677
|
}
|
|
11534
11678
|
}
|
|
11535
11679
|
function handleClineSecondary(root, results) {
|
|
11536
|
-
const rooPath =
|
|
11680
|
+
const rooPath = path33.resolve(root, ".roo", "rules", "kuma.md");
|
|
11537
11681
|
if (!results.some((r) => r.filePath === ".roo/rules/kuma.md")) {
|
|
11538
11682
|
try {
|
|
11539
|
-
const dir =
|
|
11540
|
-
if (
|
|
11541
|
-
const existing =
|
|
11683
|
+
const dir = path33.dirname(rooPath);
|
|
11684
|
+
if (fs30.existsSync(rooPath)) {
|
|
11685
|
+
const existing = fs30.readFileSync(rooPath, "utf-8");
|
|
11542
11686
|
if (existing.includes("kuma-mcp") || existing.includes("Kuma MCP")) {
|
|
11543
11687
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "skipped" });
|
|
11544
11688
|
} else {
|
|
11545
|
-
|
|
11689
|
+
fs30.writeFileSync(rooPath, existing.trimEnd() + "\n\n---\n\n" + rooRulesTemplate(), "utf-8");
|
|
11546
11690
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "appended" });
|
|
11547
11691
|
}
|
|
11548
11692
|
} else {
|
|
11549
|
-
if (!
|
|
11550
|
-
|
|
11693
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11694
|
+
fs30.writeFileSync(rooPath, rooRulesTemplate(), "utf-8");
|
|
11551
11695
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "created" });
|
|
11552
11696
|
}
|
|
11553
11697
|
} catch (err) {
|
|
@@ -11559,21 +11703,21 @@ function handleClineSecondary(root, results) {
|
|
|
11559
11703
|
});
|
|
11560
11704
|
}
|
|
11561
11705
|
}
|
|
11562
|
-
const traePath =
|
|
11706
|
+
const traePath = path33.resolve(root, ".trae", "rules", "kuma.md");
|
|
11563
11707
|
if (!results.some((r) => r.filePath === ".trae/rules/kuma.md")) {
|
|
11564
11708
|
try {
|
|
11565
|
-
const dir =
|
|
11566
|
-
if (
|
|
11567
|
-
const existing =
|
|
11709
|
+
const dir = path33.dirname(traePath);
|
|
11710
|
+
if (fs30.existsSync(traePath)) {
|
|
11711
|
+
const existing = fs30.readFileSync(traePath, "utf-8");
|
|
11568
11712
|
if (existing.includes("Kuma MCP")) {
|
|
11569
11713
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "skipped" });
|
|
11570
11714
|
} else {
|
|
11571
|
-
|
|
11715
|
+
fs30.writeFileSync(traePath, existing.trimEnd() + "\n\n---\n\n" + traeRulesTemplate(), "utf-8");
|
|
11572
11716
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "appended" });
|
|
11573
11717
|
}
|
|
11574
11718
|
} else {
|
|
11575
|
-
if (!
|
|
11576
|
-
|
|
11719
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11720
|
+
fs30.writeFileSync(traePath, traeRulesTemplate(), "utf-8");
|
|
11577
11721
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "created" });
|
|
11578
11722
|
}
|
|
11579
11723
|
} catch (err) {
|
|
@@ -11585,21 +11729,21 @@ function handleClineSecondary(root, results) {
|
|
|
11585
11729
|
});
|
|
11586
11730
|
}
|
|
11587
11731
|
}
|
|
11588
|
-
const qoderPath =
|
|
11732
|
+
const qoderPath = path33.resolve(root, ".qoder", "rules", "kuma.md");
|
|
11589
11733
|
if (!results.some((r) => r.filePath === ".qoder/rules/kuma.md")) {
|
|
11590
11734
|
try {
|
|
11591
|
-
const dir =
|
|
11592
|
-
if (
|
|
11593
|
-
const existing =
|
|
11735
|
+
const dir = path33.dirname(qoderPath);
|
|
11736
|
+
if (fs30.existsSync(qoderPath)) {
|
|
11737
|
+
const existing = fs30.readFileSync(qoderPath, "utf-8");
|
|
11594
11738
|
if (existing.includes("Kuma MCP")) {
|
|
11595
11739
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "skipped" });
|
|
11596
11740
|
} else {
|
|
11597
|
-
|
|
11741
|
+
fs30.writeFileSync(qoderPath, existing.trimEnd() + "\n\n---\n\n" + qoderRulesTemplate(), "utf-8");
|
|
11598
11742
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "appended" });
|
|
11599
11743
|
}
|
|
11600
11744
|
} else {
|
|
11601
|
-
if (!
|
|
11602
|
-
|
|
11745
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11746
|
+
fs30.writeFileSync(qoderPath, qoderRulesTemplate(), "utf-8");
|
|
11603
11747
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "created" });
|
|
11604
11748
|
}
|
|
11605
11749
|
} catch (err) {
|
|
@@ -11613,18 +11757,18 @@ function handleClineSecondary(root, results) {
|
|
|
11613
11757
|
}
|
|
11614
11758
|
}
|
|
11615
11759
|
function handleAiderSecondary(root, results) {
|
|
11616
|
-
const ymlPath =
|
|
11760
|
+
const ymlPath = path33.resolve(root, ".aider.conf.yml");
|
|
11617
11761
|
if (results.some((r) => r.filePath === ".aider.conf.yml")) return;
|
|
11618
11762
|
try {
|
|
11619
11763
|
const conventionsRef = "read: CONVENTIONS.md";
|
|
11620
|
-
if (
|
|
11621
|
-
const existingContent =
|
|
11764
|
+
if (fs30.existsSync(ymlPath)) {
|
|
11765
|
+
const existingContent = fs30.readFileSync(ymlPath, "utf-8");
|
|
11622
11766
|
if (existingContent.includes("CONVENTIONS.md") || existingContent.includes("kuma")) {
|
|
11623
11767
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "skipped" });
|
|
11624
11768
|
return;
|
|
11625
11769
|
}
|
|
11626
11770
|
const newContent = existingContent.trimEnd() + "\n\n# Kuma MCP conventions\n" + conventionsRef + "\n";
|
|
11627
|
-
|
|
11771
|
+
fs30.writeFileSync(ymlPath, newContent, "utf-8");
|
|
11628
11772
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "appended" });
|
|
11629
11773
|
} else {
|
|
11630
11774
|
const content = [
|
|
@@ -11634,7 +11778,7 @@ function handleAiderSecondary(root, results) {
|
|
|
11634
11778
|
conventionsRef,
|
|
11635
11779
|
""
|
|
11636
11780
|
].join("\n");
|
|
11637
|
-
|
|
11781
|
+
fs30.writeFileSync(ymlPath, content, "utf-8");
|
|
11638
11782
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "created" });
|
|
11639
11783
|
}
|
|
11640
11784
|
} catch (err) {
|
|
@@ -11647,10 +11791,10 @@ function handleAiderSecondary(root, results) {
|
|
|
11647
11791
|
}
|
|
11648
11792
|
}
|
|
11649
11793
|
function handleCopilotSecondary(root, results) {
|
|
11650
|
-
const skillPath =
|
|
11794
|
+
const skillPath = path33.resolve(root, ".github/skills/kuma/SKILL.md");
|
|
11651
11795
|
if (results.some((r) => r.filePath === ".github/skills/kuma/SKILL.md")) return;
|
|
11652
11796
|
try {
|
|
11653
|
-
const dir =
|
|
11797
|
+
const dir = path33.dirname(skillPath);
|
|
11654
11798
|
const content = [
|
|
11655
11799
|
"---",
|
|
11656
11800
|
"name: kuma-mcp",
|
|
@@ -11661,17 +11805,17 @@ function handleCopilotSecondary(root, results) {
|
|
|
11661
11805
|
"",
|
|
11662
11806
|
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
11663
11807
|
].join("\n");
|
|
11664
|
-
if (
|
|
11665
|
-
const existingContent =
|
|
11808
|
+
if (fs30.existsSync(skillPath)) {
|
|
11809
|
+
const existingContent = fs30.readFileSync(skillPath, "utf-8");
|
|
11666
11810
|
if (existingContent.includes("kuma")) {
|
|
11667
11811
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "skipped" });
|
|
11668
11812
|
return;
|
|
11669
11813
|
}
|
|
11670
|
-
|
|
11814
|
+
fs30.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
|
|
11671
11815
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "appended" });
|
|
11672
11816
|
} else {
|
|
11673
|
-
if (!
|
|
11674
|
-
|
|
11817
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11818
|
+
fs30.writeFileSync(skillPath, content, "utf-8");
|
|
11675
11819
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "created" });
|
|
11676
11820
|
}
|
|
11677
11821
|
} catch (err) {
|
|
@@ -11684,21 +11828,21 @@ function handleCopilotSecondary(root, results) {
|
|
|
11684
11828
|
}
|
|
11685
11829
|
}
|
|
11686
11830
|
function handleOpenclawSecondary(root, results) {
|
|
11687
|
-
const mcpPath =
|
|
11831
|
+
const mcpPath = path33.resolve(root, ".agents/mcp_config.json");
|
|
11688
11832
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
11689
11833
|
try {
|
|
11690
|
-
const dir =
|
|
11691
|
-
if (
|
|
11692
|
-
const existingContent =
|
|
11834
|
+
const dir = path33.dirname(mcpPath);
|
|
11835
|
+
if (fs30.existsSync(mcpPath)) {
|
|
11836
|
+
const existingContent = fs30.readFileSync(mcpPath, "utf-8");
|
|
11693
11837
|
if (existingContent.includes("kuma")) return;
|
|
11694
11838
|
const parsed = JSON.parse(existingContent);
|
|
11695
11839
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11696
11840
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11697
|
-
|
|
11841
|
+
fs30.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11698
11842
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
11699
11843
|
} else {
|
|
11700
|
-
if (!
|
|
11701
|
-
|
|
11844
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11845
|
+
fs30.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
11702
11846
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "created" });
|
|
11703
11847
|
}
|
|
11704
11848
|
} catch (err) {
|
|
@@ -11711,20 +11855,20 @@ function handleOpenclawSecondary(root, results) {
|
|
|
11711
11855
|
}
|
|
11712
11856
|
}
|
|
11713
11857
|
function handleInitMdGeneration(root, results) {
|
|
11714
|
-
const initMdPath =
|
|
11858
|
+
const initMdPath = path33.resolve(root, ".kuma/init.md");
|
|
11715
11859
|
try {
|
|
11716
|
-
const kumaDir =
|
|
11717
|
-
if (!
|
|
11718
|
-
if (
|
|
11719
|
-
const existing =
|
|
11860
|
+
const kumaDir = path33.dirname(initMdPath);
|
|
11861
|
+
if (!fs30.existsSync(kumaDir)) fs30.mkdirSync(kumaDir, { recursive: true });
|
|
11862
|
+
if (fs30.existsSync(initMdPath)) {
|
|
11863
|
+
const existing = fs30.readFileSync(initMdPath, "utf-8");
|
|
11720
11864
|
if (existing.includes("_Generated by Kuma MCP_")) {
|
|
11721
11865
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "skipped" });
|
|
11722
11866
|
return;
|
|
11723
11867
|
}
|
|
11724
|
-
|
|
11868
|
+
fs30.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
|
|
11725
11869
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "appended" });
|
|
11726
11870
|
} else {
|
|
11727
|
-
|
|
11871
|
+
fs30.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
|
|
11728
11872
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "created" });
|
|
11729
11873
|
}
|
|
11730
11874
|
} catch (err) {
|
|
@@ -11737,20 +11881,20 @@ function handleInitMdGeneration(root, results) {
|
|
|
11737
11881
|
}
|
|
11738
11882
|
}
|
|
11739
11883
|
function handleCodewhaleSecondary(root, results) {
|
|
11740
|
-
const mcpPath =
|
|
11884
|
+
const mcpPath = path33.resolve(root, ".codewhale/mcp.json");
|
|
11741
11885
|
if (results.some((r) => r.filePath === ".codewhale/mcp.json")) return;
|
|
11742
11886
|
try {
|
|
11743
|
-
const dir =
|
|
11744
|
-
if (
|
|
11745
|
-
const existingContent =
|
|
11887
|
+
const dir = path33.dirname(mcpPath);
|
|
11888
|
+
if (fs30.existsSync(mcpPath)) {
|
|
11889
|
+
const existingContent = fs30.readFileSync(mcpPath, "utf-8");
|
|
11746
11890
|
if (existingContent.includes("kuma")) return;
|
|
11747
11891
|
const parsed = JSON.parse(existingContent);
|
|
11748
11892
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11749
11893
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11750
|
-
|
|
11894
|
+
fs30.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11751
11895
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "appended" });
|
|
11752
11896
|
} else {
|
|
11753
|
-
if (!
|
|
11897
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11754
11898
|
const config = {
|
|
11755
11899
|
mcpServers: {
|
|
11756
11900
|
kuma: {
|
|
@@ -11760,7 +11904,7 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
11760
11904
|
}
|
|
11761
11905
|
}
|
|
11762
11906
|
};
|
|
11763
|
-
|
|
11907
|
+
fs30.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
11764
11908
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "created" });
|
|
11765
11909
|
}
|
|
11766
11910
|
} catch (err) {
|
|
@@ -11773,7 +11917,7 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
11773
11917
|
}
|
|
11774
11918
|
}
|
|
11775
11919
|
function handleClaudeSecondary(root, results) {
|
|
11776
|
-
const settingsPath =
|
|
11920
|
+
const settingsPath = path33.resolve(root, ".claude", "settings.json");
|
|
11777
11921
|
const hookBlock = {
|
|
11778
11922
|
hooks: {
|
|
11779
11923
|
PreToolUse: [
|
|
@@ -11804,8 +11948,8 @@ function handleClaudeSecondary(root, results) {
|
|
|
11804
11948
|
}
|
|
11805
11949
|
};
|
|
11806
11950
|
try {
|
|
11807
|
-
if (
|
|
11808
|
-
const existing = JSON.parse(
|
|
11951
|
+
if (fs30.existsSync(settingsPath)) {
|
|
11952
|
+
const existing = JSON.parse(fs30.readFileSync(settingsPath, "utf-8"));
|
|
11809
11953
|
const existingHooks = existing.hooks || {};
|
|
11810
11954
|
const mergedPre = [...existingHooks.PreToolUse || []];
|
|
11811
11955
|
for (const entry of hookBlock.hooks.PreToolUse) {
|
|
@@ -11829,12 +11973,12 @@ function handleClaudeSecondary(root, results) {
|
|
|
11829
11973
|
...existing,
|
|
11830
11974
|
hooks: { ...existingHooks, PreToolUse: mergedPre, SessionStart: mergedStart, PreCompact: mergedCompact }
|
|
11831
11975
|
};
|
|
11832
|
-
|
|
11976
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
11833
11977
|
results.push({ type: "claude", filePath: ".claude/settings.json", action: "appended" });
|
|
11834
11978
|
} else {
|
|
11835
|
-
const dir =
|
|
11836
|
-
if (!
|
|
11837
|
-
|
|
11979
|
+
const dir = path33.dirname(settingsPath);
|
|
11980
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11981
|
+
fs30.writeFileSync(settingsPath, JSON.stringify(hookBlock, null, 2) + "\n", "utf-8");
|
|
11838
11982
|
results.push({ type: "claude", filePath: ".claude/settings.json", action: "created" });
|
|
11839
11983
|
}
|
|
11840
11984
|
} catch (err) {
|
|
@@ -11845,10 +11989,10 @@ function handleClaudeSecondary(root, results) {
|
|
|
11845
11989
|
error: err instanceof Error ? err.message : String(err)
|
|
11846
11990
|
});
|
|
11847
11991
|
}
|
|
11848
|
-
const skillPath =
|
|
11992
|
+
const skillPath = path33.resolve(root, ".claude", "skills", "kuma", "SKILL.md");
|
|
11849
11993
|
if (!results.some((r) => r.filePath === ".claude/skills/kuma/SKILL.md")) {
|
|
11850
11994
|
try {
|
|
11851
|
-
const dir =
|
|
11995
|
+
const dir = path33.dirname(skillPath);
|
|
11852
11996
|
const content = [
|
|
11853
11997
|
"---",
|
|
11854
11998
|
"name: kuma-mcp",
|
|
@@ -11860,17 +12004,17 @@ function handleClaudeSecondary(root, results) {
|
|
|
11860
12004
|
"\u{1F4D6} Read `.kuma/init.md` for detailed rules.",
|
|
11861
12005
|
"\u{1F4C2} Readable state: `.kuma/KNOWN_GOTCHAS.md` (gotchas) \xB7 `.kuma/memories/decisions.md` (decisions)"
|
|
11862
12006
|
].join("\n");
|
|
11863
|
-
if (
|
|
11864
|
-
const existing =
|
|
12007
|
+
if (fs30.existsSync(skillPath)) {
|
|
12008
|
+
const existing = fs30.readFileSync(skillPath, "utf-8");
|
|
11865
12009
|
if (existing.includes("kuma-mcp")) {
|
|
11866
12010
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "skipped" });
|
|
11867
12011
|
} else {
|
|
11868
|
-
|
|
12012
|
+
fs30.writeFileSync(skillPath, existing.trimEnd() + "\n\n---\n\n" + content, "utf-8");
|
|
11869
12013
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "appended" });
|
|
11870
12014
|
}
|
|
11871
12015
|
} else {
|
|
11872
|
-
if (!
|
|
11873
|
-
|
|
12016
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
12017
|
+
fs30.writeFileSync(skillPath, content, "utf-8");
|
|
11874
12018
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "created" });
|
|
11875
12019
|
}
|
|
11876
12020
|
} catch (err) {
|
|
@@ -11884,27 +12028,27 @@ function handleClaudeSecondary(root, results) {
|
|
|
11884
12028
|
}
|
|
11885
12029
|
}
|
|
11886
12030
|
function writeHooksFile(root, relPath, type, results, build) {
|
|
11887
|
-
const fullPath =
|
|
12031
|
+
const fullPath = path33.resolve(root, relPath);
|
|
11888
12032
|
try {
|
|
11889
12033
|
let existing = {};
|
|
11890
12034
|
let existed = false;
|
|
11891
|
-
if (
|
|
12035
|
+
if (fs30.existsSync(fullPath)) {
|
|
11892
12036
|
existed = true;
|
|
11893
12037
|
try {
|
|
11894
|
-
existing = JSON.parse(
|
|
12038
|
+
existing = JSON.parse(fs30.readFileSync(fullPath, "utf-8"));
|
|
11895
12039
|
} catch {
|
|
11896
12040
|
existing = {};
|
|
11897
12041
|
}
|
|
11898
12042
|
} else {
|
|
11899
|
-
const dir =
|
|
11900
|
-
if (!
|
|
12043
|
+
const dir = path33.dirname(fullPath);
|
|
12044
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
11901
12045
|
}
|
|
11902
12046
|
const { doc, changed } = build(existing);
|
|
11903
12047
|
if (!changed) {
|
|
11904
12048
|
results.push({ type, filePath: relPath, action: "skipped" });
|
|
11905
12049
|
return;
|
|
11906
12050
|
}
|
|
11907
|
-
|
|
12051
|
+
fs30.writeFileSync(fullPath, JSON.stringify(doc, null, 2) + "\n", "utf-8");
|
|
11908
12052
|
results.push({ type, filePath: relPath, action: existed ? "appended" : "created" });
|
|
11909
12053
|
} catch (err) {
|
|
11910
12054
|
results.push({ type, filePath: relPath, action: "error", error: err instanceof Error ? err.message : String(err) });
|
|
@@ -11985,18 +12129,18 @@ function handleCodexHooks(root, results) {
|
|
|
11985
12129
|
});
|
|
11986
12130
|
}
|
|
11987
12131
|
function handleCursorGotchaRules(root, results) {
|
|
11988
|
-
const rulesDir2 =
|
|
12132
|
+
const rulesDir2 = path33.resolve(root, ".cursor", "rules", "kuma-gotchas");
|
|
11989
12133
|
try {
|
|
11990
12134
|
let gotchas = [];
|
|
11991
12135
|
try {
|
|
11992
12136
|
gotchas = getActiveGotchas().filter((g) => g.severity === "high" || g.severity === "critical");
|
|
11993
12137
|
} catch {
|
|
11994
12138
|
}
|
|
11995
|
-
if (
|
|
11996
|
-
for (const f of
|
|
12139
|
+
if (fs30.existsSync(rulesDir2)) {
|
|
12140
|
+
for (const f of fs30.readdirSync(rulesDir2)) {
|
|
11997
12141
|
if (f.endsWith(".mdc")) {
|
|
11998
12142
|
try {
|
|
11999
|
-
|
|
12143
|
+
fs30.rmSync(path33.join(rulesDir2, f), { force: true });
|
|
12000
12144
|
} catch {
|
|
12001
12145
|
}
|
|
12002
12146
|
}
|
|
@@ -12006,7 +12150,7 @@ function handleCursorGotchaRules(root, results) {
|
|
|
12006
12150
|
results.push({ type: "cursor", filePath: ".cursor/rules/kuma-gotchas/", action: "skipped" });
|
|
12007
12151
|
return;
|
|
12008
12152
|
}
|
|
12009
|
-
|
|
12153
|
+
fs30.mkdirSync(rulesDir2, { recursive: true });
|
|
12010
12154
|
const seen = /* @__PURE__ */ new Set();
|
|
12011
12155
|
let created = 0;
|
|
12012
12156
|
for (const g of gotchas) {
|
|
@@ -12027,7 +12171,7 @@ function handleCursorGotchaRules(root, results) {
|
|
|
12027
12171
|
"`kuma_context({ action: 'history', target: '<file>' })`",
|
|
12028
12172
|
""
|
|
12029
12173
|
].join("\n");
|
|
12030
|
-
|
|
12174
|
+
fs30.writeFileSync(path33.join(rulesDir2, `${slug2}.mdc`), md, "utf-8");
|
|
12031
12175
|
created++;
|
|
12032
12176
|
}
|
|
12033
12177
|
results.push({
|
|
@@ -12054,28 +12198,28 @@ function runInit(options) {
|
|
|
12054
12198
|
let agentsMdHandled = false;
|
|
12055
12199
|
for (const type of selected) {
|
|
12056
12200
|
const relativePath = configFilePath(type);
|
|
12057
|
-
const fullPath =
|
|
12201
|
+
const fullPath = path33.resolve(root, relativePath);
|
|
12058
12202
|
const getTemplate = TEMPLATES[type];
|
|
12059
12203
|
try {
|
|
12060
12204
|
if (AGENTS_MD_TYPES.includes(type) && !agentsMdHandled) {
|
|
12061
12205
|
agentsMdHandled = true;
|
|
12062
12206
|
const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
|
|
12063
|
-
if (
|
|
12207
|
+
if (fs30.existsSync(fullPath)) {
|
|
12064
12208
|
if (options.skipExisting) {
|
|
12065
12209
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12066
12210
|
} else {
|
|
12067
|
-
const existingContent =
|
|
12211
|
+
const existingContent = fs30.readFileSync(fullPath, "utf-8");
|
|
12068
12212
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
12069
12213
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12070
12214
|
} else {
|
|
12071
|
-
|
|
12215
|
+
fs30.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
|
|
12072
12216
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
12073
12217
|
}
|
|
12074
12218
|
}
|
|
12075
12219
|
} else {
|
|
12076
|
-
const dir =
|
|
12077
|
-
if (!
|
|
12078
|
-
|
|
12220
|
+
const dir = path33.dirname(fullPath);
|
|
12221
|
+
if (!fs30.existsSync(dir)) fs30.mkdirSync(dir, { recursive: true });
|
|
12222
|
+
fs30.writeFileSync(fullPath, combinedContent, "utf-8");
|
|
12079
12223
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
12080
12224
|
}
|
|
12081
12225
|
if (selectedSet.has("opencode")) handleOpencodeSecondary(root, results);
|
|
@@ -12087,12 +12231,12 @@ function runInit(options) {
|
|
|
12087
12231
|
continue;
|
|
12088
12232
|
} else {
|
|
12089
12233
|
const template = getTemplate();
|
|
12090
|
-
if (
|
|
12234
|
+
if (fs30.existsSync(fullPath)) {
|
|
12091
12235
|
if (options.skipExisting) {
|
|
12092
12236
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12093
12237
|
continue;
|
|
12094
12238
|
}
|
|
12095
|
-
const existingContent =
|
|
12239
|
+
const existingContent = fs30.readFileSync(fullPath, "utf-8");
|
|
12096
12240
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
12097
12241
|
if (type === "antigravity") {
|
|
12098
12242
|
handleAntigravitySecondary(root, results);
|
|
@@ -12109,14 +12253,14 @@ function runInit(options) {
|
|
|
12109
12253
|
continue;
|
|
12110
12254
|
}
|
|
12111
12255
|
const newContent = existingContent.trimEnd() + APPEND_SEPARATOR + template;
|
|
12112
|
-
|
|
12256
|
+
fs30.writeFileSync(fullPath, newContent, "utf-8");
|
|
12113
12257
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
12114
12258
|
} else {
|
|
12115
|
-
const dir =
|
|
12116
|
-
if (!
|
|
12117
|
-
|
|
12259
|
+
const dir = path33.dirname(fullPath);
|
|
12260
|
+
if (!fs30.existsSync(dir)) {
|
|
12261
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12118
12262
|
}
|
|
12119
|
-
|
|
12263
|
+
fs30.writeFileSync(fullPath, template, "utf-8");
|
|
12120
12264
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
12121
12265
|
}
|
|
12122
12266
|
if (type === "antigravity") {
|
|
@@ -12288,8 +12432,8 @@ var compactionContext_exports = {};
|
|
|
12288
12432
|
__export(compactionContext_exports, {
|
|
12289
12433
|
getCompactionContext: () => getCompactionContext
|
|
12290
12434
|
});
|
|
12291
|
-
import
|
|
12292
|
-
import
|
|
12435
|
+
import fs31 from "fs";
|
|
12436
|
+
import path34 from "path";
|
|
12293
12437
|
async function getCompactionContext() {
|
|
12294
12438
|
const parts = [];
|
|
12295
12439
|
try {
|
|
@@ -12302,8 +12446,8 @@ async function getCompactionContext() {
|
|
|
12302
12446
|
} catch {
|
|
12303
12447
|
}
|
|
12304
12448
|
try {
|
|
12305
|
-
const dbFile =
|
|
12306
|
-
if (
|
|
12449
|
+
const dbFile = path34.join(getProjectRoot(), ".kuma", "kuma.db");
|
|
12450
|
+
if (fs31.existsSync(dbFile)) {
|
|
12307
12451
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
12308
12452
|
const db = await getDb2();
|
|
12309
12453
|
const stmt = db.prepare(
|
|
@@ -12329,9 +12473,9 @@ async function getCompactionContext() {
|
|
|
12329
12473
|
} catch {
|
|
12330
12474
|
}
|
|
12331
12475
|
try {
|
|
12332
|
-
const cpDir =
|
|
12333
|
-
if (
|
|
12334
|
-
const cps =
|
|
12476
|
+
const cpDir = path34.join(getProjectRoot(), ".kuma", "checkpoints");
|
|
12477
|
+
if (fs31.existsSync(cpDir)) {
|
|
12478
|
+
const cps = fs31.readdirSync(cpDir).slice(0, 3);
|
|
12335
12479
|
if (cps.length > 0) parts.push(`Checkpoints: ${cps.join(", ")}`);
|
|
12336
12480
|
}
|
|
12337
12481
|
} catch {
|
|
@@ -12349,6 +12493,190 @@ var init_compactionContext = __esm({
|
|
|
12349
12493
|
}
|
|
12350
12494
|
});
|
|
12351
12495
|
|
|
12496
|
+
// src/engine/kumaDaemon.ts
|
|
12497
|
+
var kumaDaemon_exports = {};
|
|
12498
|
+
__export(kumaDaemon_exports, {
|
|
12499
|
+
DEFAULT_INTERVAL_MS: () => DEFAULT_INTERVAL_MS,
|
|
12500
|
+
daemonRunChild: () => daemonRunChild,
|
|
12501
|
+
daemonStart: () => daemonStart,
|
|
12502
|
+
daemonStatus: () => daemonStatus,
|
|
12503
|
+
daemonStop: () => daemonStop,
|
|
12504
|
+
daemonTick: () => daemonTick
|
|
12505
|
+
});
|
|
12506
|
+
import fs32 from "fs";
|
|
12507
|
+
import path35 from "path";
|
|
12508
|
+
import { spawn } from "child_process";
|
|
12509
|
+
function paths(root) {
|
|
12510
|
+
const r = root || getProjectRoot();
|
|
12511
|
+
return {
|
|
12512
|
+
pid: path35.join(r, PID_FILE),
|
|
12513
|
+
status: path35.join(r, STATUS_FILE),
|
|
12514
|
+
log: path35.join(r, LOG_FILE)
|
|
12515
|
+
};
|
|
12516
|
+
}
|
|
12517
|
+
function readPid(root) {
|
|
12518
|
+
try {
|
|
12519
|
+
const fp = paths(root).pid;
|
|
12520
|
+
if (!fs32.existsSync(fp)) return null;
|
|
12521
|
+
const pid = Number(fs32.readFileSync(fp, "utf-8").trim());
|
|
12522
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
12523
|
+
} catch {
|
|
12524
|
+
return null;
|
|
12525
|
+
}
|
|
12526
|
+
}
|
|
12527
|
+
function isAlive(pid) {
|
|
12528
|
+
try {
|
|
12529
|
+
process.kill(pid, 0);
|
|
12530
|
+
return true;
|
|
12531
|
+
} catch {
|
|
12532
|
+
return false;
|
|
12533
|
+
}
|
|
12534
|
+
}
|
|
12535
|
+
async function daemonTick(root) {
|
|
12536
|
+
const r = root || getProjectRoot();
|
|
12537
|
+
const p = paths(r);
|
|
12538
|
+
const notes = [];
|
|
12539
|
+
try {
|
|
12540
|
+
const { syncModifiedFiles: syncModifiedFiles2 } = await Promise.resolve().then(() => (init_kumaCodeScanner(), kumaCodeScanner_exports));
|
|
12541
|
+
const scan = await syncModifiedFiles2(50);
|
|
12542
|
+
notes.push(`map: ${scan.filesScanned}f/${scan.nodeCount}n/${scan.edgeCount}e`);
|
|
12543
|
+
} catch (err) {
|
|
12544
|
+
notes.push(`map: skipped (${err})`);
|
|
12545
|
+
}
|
|
12546
|
+
try {
|
|
12547
|
+
const { refreshPackageBriefs: refreshPackageBriefs2 } = await Promise.resolve().then(() => (init_packageBriefs(), packageBriefs_exports));
|
|
12548
|
+
const { count } = await refreshPackageBriefs2();
|
|
12549
|
+
notes.push(`briefs: ${count}`);
|
|
12550
|
+
} catch {
|
|
12551
|
+
notes.push("briefs: skipped");
|
|
12552
|
+
}
|
|
12553
|
+
try {
|
|
12554
|
+
const { detectDrift: detectDrift2 } = await Promise.resolve().then(() => (init_kumaDriftDetector(), kumaDriftDetector_exports));
|
|
12555
|
+
const records = await detectDrift2();
|
|
12556
|
+
const stale = records.filter((x) => x.severity === "stale" || x.severity === "missing").length;
|
|
12557
|
+
notes.push(`drift: ${stale}/${records.length} stale`);
|
|
12558
|
+
} catch {
|
|
12559
|
+
notes.push("drift: skipped");
|
|
12560
|
+
}
|
|
12561
|
+
try {
|
|
12562
|
+
const { clearDirty: clearDirty2 } = await Promise.resolve().then(() => (init_cacheFreshness(), cacheFreshness_exports));
|
|
12563
|
+
clearDirty2();
|
|
12564
|
+
} catch {
|
|
12565
|
+
}
|
|
12566
|
+
const status = {
|
|
12567
|
+
lastRunAt: Date.now(),
|
|
12568
|
+
lastResult: notes.join(" \xB7 ")
|
|
12569
|
+
};
|
|
12570
|
+
try {
|
|
12571
|
+
let prev = {};
|
|
12572
|
+
if (fs32.existsSync(p.status)) prev = JSON.parse(fs32.readFileSync(p.status, "utf-8"));
|
|
12573
|
+
fs32.writeFileSync(p.status, JSON.stringify({ ...prev, ...status }, null, 2), "utf-8");
|
|
12574
|
+
} catch {
|
|
12575
|
+
}
|
|
12576
|
+
return notes.join(" \xB7 ");
|
|
12577
|
+
}
|
|
12578
|
+
async function runLoop(intervalMs) {
|
|
12579
|
+
const r = getProjectRoot();
|
|
12580
|
+
const p = paths(r);
|
|
12581
|
+
const log = (msg) => {
|
|
12582
|
+
try {
|
|
12583
|
+
fs32.appendFileSync(p.log, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
12584
|
+
`, "utf-8");
|
|
12585
|
+
} catch {
|
|
12586
|
+
}
|
|
12587
|
+
};
|
|
12588
|
+
log(`daemon started (interval ${intervalMs}ms)`);
|
|
12589
|
+
for (; ; ) {
|
|
12590
|
+
try {
|
|
12591
|
+
const res = await daemonTick(r);
|
|
12592
|
+
log(`tick: ${res}`);
|
|
12593
|
+
} catch (err) {
|
|
12594
|
+
log(`tick failed: ${err}`);
|
|
12595
|
+
}
|
|
12596
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12597
|
+
}
|
|
12598
|
+
}
|
|
12599
|
+
async function daemonStart(intervalMs = DEFAULT_INTERVAL_MS, root) {
|
|
12600
|
+
const r = root || getProjectRoot();
|
|
12601
|
+
const p = paths(r);
|
|
12602
|
+
const existing = readPid(r);
|
|
12603
|
+
if (existing && isAlive(existing)) {
|
|
12604
|
+
return `\u{1F43B} Daemon already running (pid ${existing}). Use \`kuma daemon stop\` first.`;
|
|
12605
|
+
}
|
|
12606
|
+
const entry = process.argv[1];
|
|
12607
|
+
const child = spawn(process.execPath, [entry, "daemon", "run", String(intervalMs)], {
|
|
12608
|
+
cwd: r,
|
|
12609
|
+
detached: true,
|
|
12610
|
+
stdio: "ignore"
|
|
12611
|
+
});
|
|
12612
|
+
child.unref();
|
|
12613
|
+
try {
|
|
12614
|
+
fs32.writeFileSync(p.pid, String(child.pid), "utf-8");
|
|
12615
|
+
fs32.writeFileSync(
|
|
12616
|
+
p.status,
|
|
12617
|
+
JSON.stringify({ running: true, pid: child.pid, startedAt: Date.now(), lastRunAt: null, lastResult: null, intervalMs }, null, 2),
|
|
12618
|
+
"utf-8"
|
|
12619
|
+
);
|
|
12620
|
+
} catch {
|
|
12621
|
+
}
|
|
12622
|
+
return `\u{1F43B} Daemon started (pid ${child.pid}, every ${Math.round(intervalMs / 6e4)}m). Status: .kuma/daemon-status.json`;
|
|
12623
|
+
}
|
|
12624
|
+
async function daemonStop(root) {
|
|
12625
|
+
const r = root || getProjectRoot();
|
|
12626
|
+
const p = paths(r);
|
|
12627
|
+
const pid = readPid(r);
|
|
12628
|
+
if (!pid) return "\u{1F43B} Daemon not running (no pid file).";
|
|
12629
|
+
if (!isAlive(pid)) {
|
|
12630
|
+
try {
|
|
12631
|
+
fs32.rmSync(p.pid, { force: true });
|
|
12632
|
+
} catch {
|
|
12633
|
+
}
|
|
12634
|
+
return "\u{1F43B} Daemon pid file was stale \u2014 cleaned up (process already gone).";
|
|
12635
|
+
}
|
|
12636
|
+
try {
|
|
12637
|
+
process.kill(pid, "SIGTERM");
|
|
12638
|
+
try {
|
|
12639
|
+
fs32.rmSync(p.pid, { force: true });
|
|
12640
|
+
} catch {
|
|
12641
|
+
}
|
|
12642
|
+
return `\u{1F43B} Daemon stopped (was pid ${pid}).`;
|
|
12643
|
+
} catch (err) {
|
|
12644
|
+
return `\u{1F43B} Could not stop daemon pid ${pid}: ${err}`;
|
|
12645
|
+
}
|
|
12646
|
+
}
|
|
12647
|
+
async function daemonStatus(root) {
|
|
12648
|
+
const r = root || getProjectRoot();
|
|
12649
|
+
const p = paths(r);
|
|
12650
|
+
const pid = readPid(r);
|
|
12651
|
+
let status = { running: false, pid: null, startedAt: null, lastRunAt: null, lastResult: null, intervalMs: DEFAULT_INTERVAL_MS };
|
|
12652
|
+
try {
|
|
12653
|
+
if (fs32.existsSync(p.status)) status = { ...status, ...JSON.parse(fs32.readFileSync(p.status, "utf-8")) };
|
|
12654
|
+
} catch {
|
|
12655
|
+
}
|
|
12656
|
+
const alive = pid !== null && isAlive(pid);
|
|
12657
|
+
status.running = alive;
|
|
12658
|
+
status.pid = alive ? pid : null;
|
|
12659
|
+
if (!alive) {
|
|
12660
|
+
return `\u{1F43B} Daemon: stopped.${status.lastRunAt ? ` Last tick ${new Date(status.lastRunAt).toISOString()}: ${status.lastResult || "n/a"}` : " Never ran."} Start with \`kuma daemon start\`.`;
|
|
12661
|
+
}
|
|
12662
|
+
return `\u{1F43B} Daemon: running (pid ${pid}).${status.lastRunAt ? ` Last tick ${new Date(status.lastRunAt).toISOString()}: ${status.lastResult || "n/a"}` : " Warming up\u2026"}`;
|
|
12663
|
+
}
|
|
12664
|
+
async function daemonRunChild(intervalMs) {
|
|
12665
|
+
const ms = Number.isFinite(intervalMs) && intervalMs >= 3e4 ? intervalMs : DEFAULT_INTERVAL_MS;
|
|
12666
|
+
await runLoop(ms);
|
|
12667
|
+
}
|
|
12668
|
+
var PID_FILE, STATUS_FILE, LOG_FILE, DEFAULT_INTERVAL_MS;
|
|
12669
|
+
var init_kumaDaemon = __esm({
|
|
12670
|
+
"src/engine/kumaDaemon.ts"() {
|
|
12671
|
+
"use strict";
|
|
12672
|
+
init_pathValidator();
|
|
12673
|
+
PID_FILE = ".kuma/daemon.pid";
|
|
12674
|
+
STATUS_FILE = ".kuma/daemon-status.json";
|
|
12675
|
+
LOG_FILE = ".kuma/daemon.log";
|
|
12676
|
+
DEFAULT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
12677
|
+
}
|
|
12678
|
+
});
|
|
12679
|
+
|
|
12352
12680
|
// src/utils/agentDetector.ts
|
|
12353
12681
|
var agentDetector_exports = {};
|
|
12354
12682
|
__export(agentDetector_exports, {
|
|
@@ -12357,19 +12685,19 @@ __export(agentDetector_exports, {
|
|
|
12357
12685
|
getSkillPath: () => getSkillPath,
|
|
12358
12686
|
skillExists: () => skillExists
|
|
12359
12687
|
});
|
|
12360
|
-
import
|
|
12361
|
-
import
|
|
12688
|
+
import fs33 from "fs";
|
|
12689
|
+
import path36 from "path";
|
|
12362
12690
|
function checkFile(root, filePath) {
|
|
12363
12691
|
try {
|
|
12364
|
-
return
|
|
12692
|
+
return fs33.existsSync(path36.join(root, filePath));
|
|
12365
12693
|
} catch {
|
|
12366
12694
|
return false;
|
|
12367
12695
|
}
|
|
12368
12696
|
}
|
|
12369
12697
|
function checkDir(root, dirPath) {
|
|
12370
12698
|
try {
|
|
12371
|
-
const fullPath =
|
|
12372
|
-
return
|
|
12699
|
+
const fullPath = path36.join(root, dirPath);
|
|
12700
|
+
return fs33.existsSync(fullPath) && fs33.statSync(fullPath).isDirectory();
|
|
12373
12701
|
} catch {
|
|
12374
12702
|
return false;
|
|
12375
12703
|
}
|
|
@@ -12437,7 +12765,7 @@ function getAgentLabel(type) {
|
|
|
12437
12765
|
function skillExists(type, projectRoot) {
|
|
12438
12766
|
const root = projectRoot ?? getProjectRoot();
|
|
12439
12767
|
const skillPath = getSkillPath(type);
|
|
12440
|
-
return
|
|
12768
|
+
return fs33.existsSync(path36.join(root, skillPath));
|
|
12441
12769
|
}
|
|
12442
12770
|
var AGENT_DETECTORS;
|
|
12443
12771
|
var init_agentDetector = __esm({
|
|
@@ -13033,8 +13361,8 @@ init_kumaGraph();
|
|
|
13033
13361
|
init_kumaMemory();
|
|
13034
13362
|
init_kumaDriftDetector();
|
|
13035
13363
|
init_pathValidator();
|
|
13036
|
-
import
|
|
13037
|
-
import
|
|
13364
|
+
import fs22 from "fs";
|
|
13365
|
+
import path25 from "path";
|
|
13038
13366
|
var CONTEXT_ALIASES = {
|
|
13039
13367
|
"research": "research",
|
|
13040
13368
|
"search": "research",
|
|
@@ -13451,10 +13779,10 @@ async function handleHistory(params) {
|
|
|
13451
13779
|
lines.push(traceFormatted);
|
|
13452
13780
|
} else {
|
|
13453
13781
|
try {
|
|
13454
|
-
const { execSync:
|
|
13782
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
13455
13783
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
13456
13784
|
const root = getProjectRoot2();
|
|
13457
|
-
const gitHistory =
|
|
13785
|
+
const gitHistory = execSync10(
|
|
13458
13786
|
`git log -n 5 --pretty=format:" \u2022 %h (%ad) %s" --date=short -- "${target}"`,
|
|
13459
13787
|
{ cwd: root, encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
13460
13788
|
).trim();
|
|
@@ -13486,7 +13814,7 @@ ${gitHistory}`);
|
|
|
13486
13814
|
try {
|
|
13487
13815
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
13488
13816
|
const db = await getDb2();
|
|
13489
|
-
const base =
|
|
13817
|
+
const base = path25.basename(target);
|
|
13490
13818
|
const stmt = db.prepare(`
|
|
13491
13819
|
SELECT description, last_verified_at FROM known_gotchas
|
|
13492
13820
|
WHERE status = 'resolved' AND (file_path LIKE ? OR file_path LIKE ?)
|
|
@@ -13499,9 +13827,9 @@ ${gitHistory}`);
|
|
|
13499
13827
|
if (resolved.length === 0) {
|
|
13500
13828
|
try {
|
|
13501
13829
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
13502
|
-
const mdPath =
|
|
13503
|
-
if (
|
|
13504
|
-
const mdContent =
|
|
13830
|
+
const mdPath = path25.join(getProjectRoot2(), ".kuma", "KNOWN_GOTCHAS.md");
|
|
13831
|
+
if (fs22.existsSync(mdPath)) {
|
|
13832
|
+
const mdContent = fs22.readFileSync(mdPath, "utf-8");
|
|
13505
13833
|
const sections = mdContent.split(/^###\s+/m);
|
|
13506
13834
|
for (const sec of sections) {
|
|
13507
13835
|
const secLower = sec.toLowerCase();
|
|
@@ -13573,8 +13901,8 @@ init_kumaDb();
|
|
|
13573
13901
|
init_kumaMemory();
|
|
13574
13902
|
init_pathValidator();
|
|
13575
13903
|
init_pathValidator();
|
|
13576
|
-
import
|
|
13577
|
-
import
|
|
13904
|
+
import fs24 from "fs";
|
|
13905
|
+
import path27 from "path";
|
|
13578
13906
|
var MEMORY_ALIASES = {
|
|
13579
13907
|
"decision": "decision",
|
|
13580
13908
|
"adr": "decision",
|
|
@@ -13685,9 +14013,9 @@ async function handleResearchSave(params) {
|
|
|
13685
14013
|
} catch {
|
|
13686
14014
|
}
|
|
13687
14015
|
try {
|
|
13688
|
-
const researchDir =
|
|
13689
|
-
if (!
|
|
13690
|
-
|
|
14016
|
+
const researchDir = path27.join(getProjectRoot(), ".kuma", "research");
|
|
14017
|
+
if (!fs24.existsSync(researchDir)) fs24.mkdirSync(researchDir, { recursive: true });
|
|
14018
|
+
fs24.writeFileSync(path27.join(researchDir, `${scope}.json`), JSON.stringify(JSON.parse(record), null, 2), "utf-8");
|
|
13691
14019
|
} catch {
|
|
13692
14020
|
}
|
|
13693
14021
|
sessionMemory.recordMemoryAction("research_save");
|
|
@@ -13883,9 +14211,9 @@ init_sessionMemory();
|
|
|
13883
14211
|
init_pathValidator();
|
|
13884
14212
|
init_sessionMemory();
|
|
13885
14213
|
init_kumaShared();
|
|
13886
|
-
import
|
|
13887
|
-
import
|
|
13888
|
-
import { execSync as
|
|
14214
|
+
import fs25 from "fs";
|
|
14215
|
+
import path28 from "path";
|
|
14216
|
+
import { execSync as execSync7 } from "child_process";
|
|
13889
14217
|
var SCRIPT_PATCH_PATTERNS = [
|
|
13890
14218
|
"writeFileSync",
|
|
13891
14219
|
"writeFile",
|
|
@@ -13911,20 +14239,20 @@ function findPatchScripts(projectRoot) {
|
|
|
13911
14239
|
const warnings = [];
|
|
13912
14240
|
const recentFiles = scanRecentFiles(projectRoot);
|
|
13913
14241
|
for (const file of recentFiles) {
|
|
13914
|
-
const ext =
|
|
14242
|
+
const ext = path28.extname(file).toLowerCase();
|
|
13915
14243
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
13916
|
-
const relativePath =
|
|
14244
|
+
const relativePath = path28.relative(projectRoot, file);
|
|
13917
14245
|
if (relativePath.startsWith("src") || relativePath.startsWith("test") || relativePath.startsWith("node_modules") || relativePath.startsWith(".") || relativePath.startsWith(".kuma/scratch")) {
|
|
13918
14246
|
continue;
|
|
13919
14247
|
}
|
|
13920
14248
|
try {
|
|
13921
|
-
const content =
|
|
14249
|
+
const content = fs25.readFileSync(file, "utf-8").toLowerCase();
|
|
13922
14250
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
13923
14251
|
if (matchedPattern) {
|
|
13924
14252
|
warnings.push({
|
|
13925
14253
|
severity: "high",
|
|
13926
14254
|
pattern: "script-patching",
|
|
13927
|
-
message: `Created script file that modifies other files: ${
|
|
14255
|
+
message: `Created script file that modifies other files: ${path28.basename(file)}`,
|
|
13928
14256
|
suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
|
|
13929
14257
|
evidence: `File: ${relativePath} contains '${matchedPattern}'`,
|
|
13930
14258
|
filePath: relativePath
|
|
@@ -13958,15 +14286,15 @@ function detectBashGrepUsage() {
|
|
|
13958
14286
|
function scanRecentFiles(projectRoot) {
|
|
13959
14287
|
const recent = [];
|
|
13960
14288
|
try {
|
|
13961
|
-
const entries =
|
|
14289
|
+
const entries = fs25.readdirSync(projectRoot, { withFileTypes: true });
|
|
13962
14290
|
const now = Date.now();
|
|
13963
14291
|
const recentThreshold = 30 * 60 * 1e3;
|
|
13964
14292
|
for (const entry of entries) {
|
|
13965
14293
|
if (!entry.isFile()) continue;
|
|
13966
14294
|
try {
|
|
13967
|
-
const stat =
|
|
14295
|
+
const stat = fs25.statSync(path28.join(projectRoot, entry.name));
|
|
13968
14296
|
if (now - stat.mtimeMs < recentThreshold || now - stat.ctimeMs < recentThreshold) {
|
|
13969
|
-
recent.push(
|
|
14297
|
+
recent.push(path28.join(projectRoot, entry.name));
|
|
13970
14298
|
}
|
|
13971
14299
|
} catch {
|
|
13972
14300
|
continue;
|
|
@@ -13979,7 +14307,7 @@ function scanRecentFiles(projectRoot) {
|
|
|
13979
14307
|
function detectGitPatchScripts(projectRoot) {
|
|
13980
14308
|
const warnings = [];
|
|
13981
14309
|
try {
|
|
13982
|
-
const statusStdout =
|
|
14310
|
+
const statusStdout = execSync7("git status --porcelain", {
|
|
13983
14311
|
cwd: projectRoot,
|
|
13984
14312
|
encoding: "utf-8",
|
|
13985
14313
|
timeout: 3e3
|
|
@@ -13990,14 +14318,14 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
13990
14318
|
const prefix = line.substring(0, 2);
|
|
13991
14319
|
if (prefix !== "??" && prefix !== "A ") continue;
|
|
13992
14320
|
const file = line.substring(3).trim();
|
|
13993
|
-
const ext =
|
|
14321
|
+
const ext = path28.extname(file).toLowerCase();
|
|
13994
14322
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
13995
14323
|
const isRootLevel = !file.includes("/");
|
|
13996
14324
|
const isScriptsDir = file.startsWith("scripts/") || file.startsWith("patches/");
|
|
13997
14325
|
if (!isRootLevel && !isScriptsDir) continue;
|
|
13998
|
-
const fullPath =
|
|
13999
|
-
if (
|
|
14000
|
-
const content =
|
|
14326
|
+
const fullPath = path28.join(projectRoot, file);
|
|
14327
|
+
if (fs25.existsSync(fullPath)) {
|
|
14328
|
+
const content = fs25.readFileSync(fullPath, "utf-8").toLowerCase();
|
|
14001
14329
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
14002
14330
|
if (matchedPattern) {
|
|
14003
14331
|
warnings.push({
|
|
@@ -14012,7 +14340,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
14012
14340
|
}
|
|
14013
14341
|
}
|
|
14014
14342
|
}
|
|
14015
|
-
const deletedStdout =
|
|
14343
|
+
const deletedStdout = execSync7("git diff --name-only --diff-filter=D HEAD", {
|
|
14016
14344
|
cwd: projectRoot,
|
|
14017
14345
|
encoding: "utf-8",
|
|
14018
14346
|
timeout: 3e3
|
|
@@ -14020,7 +14348,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
14020
14348
|
if (deletedStdout) {
|
|
14021
14349
|
const deletedFiles = deletedStdout.split("\n").filter(Boolean);
|
|
14022
14350
|
for (const file of deletedFiles) {
|
|
14023
|
-
const ext =
|
|
14351
|
+
const ext = path28.extname(file).toLowerCase();
|
|
14024
14352
|
if (SCRIPT_EXTENSIONS.includes(ext)) {
|
|
14025
14353
|
warnings.push({
|
|
14026
14354
|
severity: "high",
|
|
@@ -14082,21 +14410,21 @@ function detectAllAntiPatterns() {
|
|
|
14082
14410
|
// src/engine/contextSnapshot.ts
|
|
14083
14411
|
init_sessionMemory();
|
|
14084
14412
|
init_pathValidator();
|
|
14085
|
-
import
|
|
14086
|
-
import
|
|
14087
|
-
import { execSync as
|
|
14413
|
+
import fs26 from "fs";
|
|
14414
|
+
import path29 from "path";
|
|
14415
|
+
import { execSync as execSync8 } from "child_process";
|
|
14088
14416
|
var SNAPSHOT_DIR = "context-snapshots";
|
|
14089
14417
|
function snapshotDir() {
|
|
14090
|
-
return
|
|
14418
|
+
return path29.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
|
|
14091
14419
|
}
|
|
14092
14420
|
function ensureSnapshotDir() {
|
|
14093
14421
|
const dir = snapshotDir();
|
|
14094
|
-
if (!
|
|
14095
|
-
|
|
14422
|
+
if (!fs26.existsSync(dir)) {
|
|
14423
|
+
fs26.mkdirSync(dir, { recursive: true });
|
|
14096
14424
|
}
|
|
14097
14425
|
}
|
|
14098
14426
|
function snapshotFilePath(timestamp) {
|
|
14099
|
-
return
|
|
14427
|
+
return path29.join(snapshotDir(), `${timestamp}.json`);
|
|
14100
14428
|
}
|
|
14101
14429
|
function saveSnapshot(goal) {
|
|
14102
14430
|
try {
|
|
@@ -14117,7 +14445,7 @@ function saveSnapshot(goal) {
|
|
|
14117
14445
|
hasConventions: !!summary.hasConventions
|
|
14118
14446
|
};
|
|
14119
14447
|
try {
|
|
14120
|
-
|
|
14448
|
+
fs26.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
14121
14449
|
} catch {
|
|
14122
14450
|
}
|
|
14123
14451
|
sessionMemory.recordToolCall("kuma_context", { action: "save", goal: snapshot.goal });
|
|
@@ -14156,7 +14484,7 @@ function formatSnapshot(snapshot) {
|
|
|
14156
14484
|
function getGitDiffStat2() {
|
|
14157
14485
|
try {
|
|
14158
14486
|
const root = getProjectRoot();
|
|
14159
|
-
const stdout =
|
|
14487
|
+
const stdout = execSync8("git diff --stat", {
|
|
14160
14488
|
cwd: root,
|
|
14161
14489
|
encoding: "utf-8",
|
|
14162
14490
|
timeout: 3e3
|
|
@@ -14817,6 +15145,7 @@ Usage:
|
|
|
14817
15145
|
npx @plumpslabs/kuma hook pre-compact PreCompact hook \u2014 survival set (goal + critical gotchas + dirty)
|
|
14818
15146
|
npx @plumpslabs/kuma hook post-commit Git post-commit \u2014 periodic incremental map sync (no rebuild)
|
|
14819
15147
|
npx @plumpslabs/kuma hook install-git Install the git post-commit hook (idempotent)
|
|
15148
|
+
npx @plumpslabs/kuma daemon start|stop|status Background sensors (map + briefs + drift) every 5m
|
|
14820
15149
|
|
|
14821
15150
|
Available config files:
|
|
14822
15151
|
--claude CLAUDE.md (Claude Code)
|
|
@@ -14909,14 +15238,14 @@ async function main() {
|
|
|
14909
15238
|
console.error("");
|
|
14910
15239
|
let killedCount = 0;
|
|
14911
15240
|
try {
|
|
14912
|
-
const
|
|
14913
|
-
const
|
|
14914
|
-
const lockDir =
|
|
14915
|
-
if (
|
|
14916
|
-
const pidFile =
|
|
14917
|
-
if (
|
|
15241
|
+
const path37 = await import("path");
|
|
15242
|
+
const fs34 = await import("fs");
|
|
15243
|
+
const lockDir = path37.resolve(process.cwd(), ".kuma/verifier.lock");
|
|
15244
|
+
if (fs34.existsSync(lockDir)) {
|
|
15245
|
+
const pidFile = path37.join(lockDir, "pid");
|
|
15246
|
+
if (fs34.existsSync(pidFile)) {
|
|
14918
15247
|
try {
|
|
14919
|
-
const pid = parseInt(
|
|
15248
|
+
const pid = parseInt(fs34.readFileSync(pidFile, "utf-8"), 10);
|
|
14920
15249
|
if (pid && pid !== process.pid) {
|
|
14921
15250
|
try {
|
|
14922
15251
|
process.kill(-pid, "SIGKILL");
|
|
@@ -14932,7 +15261,7 @@ async function main() {
|
|
|
14932
15261
|
} catch {
|
|
14933
15262
|
}
|
|
14934
15263
|
}
|
|
14935
|
-
|
|
15264
|
+
fs34.rmSync(lockDir, { recursive: true, force: true });
|
|
14936
15265
|
}
|
|
14937
15266
|
if (killedCount === 0) {
|
|
14938
15267
|
console.error("\u2705 No running verifications found to kill.");
|
|
@@ -14940,25 +15269,25 @@ async function main() {
|
|
|
14940
15269
|
} catch {
|
|
14941
15270
|
}
|
|
14942
15271
|
try {
|
|
14943
|
-
const { execSync:
|
|
15272
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
14944
15273
|
try {
|
|
14945
|
-
|
|
15274
|
+
execSync10("pkill -f 'jest' 2>/dev/null || true");
|
|
14946
15275
|
} catch {
|
|
14947
15276
|
}
|
|
14948
15277
|
try {
|
|
14949
|
-
|
|
15278
|
+
execSync10("pkill -f 'jest-worker' 2>/dev/null || true");
|
|
14950
15279
|
} catch {
|
|
14951
15280
|
}
|
|
14952
15281
|
try {
|
|
14953
|
-
|
|
15282
|
+
execSync10("pkill -f 'pnpm test' 2>/dev/null || true");
|
|
14954
15283
|
} catch {
|
|
14955
15284
|
}
|
|
14956
15285
|
try {
|
|
14957
|
-
|
|
15286
|
+
execSync10("pkill -f 'npm test' 2>/dev/null || true");
|
|
14958
15287
|
} catch {
|
|
14959
15288
|
}
|
|
14960
15289
|
try {
|
|
14961
|
-
|
|
15290
|
+
execSync10("pkill -f 'yarn test' 2>/dev/null || true");
|
|
14962
15291
|
} catch {
|
|
14963
15292
|
}
|
|
14964
15293
|
console.error("\u2705 Killed orphaned test processes");
|
|
@@ -14987,7 +15316,7 @@ async function main() {
|
|
|
14987
15316
|
if (args[0] === "studio") {
|
|
14988
15317
|
const { fileURLToPath } = await import("url");
|
|
14989
15318
|
const { dirname, join } = await import("path");
|
|
14990
|
-
const { spawn } = await import("child_process");
|
|
15319
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
14991
15320
|
const { existsSync } = await import("fs");
|
|
14992
15321
|
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
14993
15322
|
const studioPath = join(distDir, "..", "packages", "ide", "studio", "dist", "index.js");
|
|
@@ -14998,7 +15327,7 @@ async function main() {
|
|
|
14998
15327
|
process.exit(1);
|
|
14999
15328
|
}
|
|
15000
15329
|
const studioArgs = args.slice(1);
|
|
15001
|
-
const child =
|
|
15330
|
+
const child = spawn2("node", [studioPath, ...studioArgs], {
|
|
15002
15331
|
stdio: "inherit",
|
|
15003
15332
|
cwd: process.cwd()
|
|
15004
15333
|
});
|
|
@@ -15131,14 +15460,14 @@ ${context}` : "";
|
|
|
15131
15460
|
process.stdout.write("\u{1F43B} [Kuma] git post-commit hook already installed.");
|
|
15132
15461
|
} else {
|
|
15133
15462
|
fsMod.appendFileSync(hookPath, `
|
|
15134
|
-
# Kuma periodic mapping
|
|
15463
|
+
# Kuma periodic mapping
|
|
15135
15464
|
${line}
|
|
15136
15465
|
`, "utf-8");
|
|
15137
15466
|
process.stdout.write("\u{1F43B} [Kuma] appended to existing git post-commit hook.");
|
|
15138
15467
|
}
|
|
15139
15468
|
} else {
|
|
15140
15469
|
fsMod.writeFileSync(hookPath, `#!/bin/sh
|
|
15141
|
-
# Kuma periodic mapping
|
|
15470
|
+
# Kuma periodic mapping
|
|
15142
15471
|
${line}
|
|
15143
15472
|
`, { mode: 493 });
|
|
15144
15473
|
process.stdout.write("\u{1F43B} [Kuma] git post-commit hook installed.");
|
|
@@ -15148,6 +15477,26 @@ ${line}
|
|
|
15148
15477
|
}
|
|
15149
15478
|
process.exit(0);
|
|
15150
15479
|
}
|
|
15480
|
+
if (args[0] === "daemon") {
|
|
15481
|
+
const sub = args[1] || "status";
|
|
15482
|
+
try {
|
|
15483
|
+
const { daemonStart: daemonStart2, daemonStop: daemonStop2, daemonStatus: daemonStatus2, daemonRunChild: daemonRunChild2, DEFAULT_INTERVAL_MS: DEFAULT_INTERVAL_MS2 } = await Promise.resolve().then(() => (init_kumaDaemon(), kumaDaemon_exports));
|
|
15484
|
+
if (sub === "start") {
|
|
15485
|
+
const ms = Number(args[2]) > 0 ? Number(args[2]) : DEFAULT_INTERVAL_MS2;
|
|
15486
|
+
process.stdout.write(await daemonStart2(ms));
|
|
15487
|
+
} else if (sub === "stop") {
|
|
15488
|
+
process.stdout.write(await daemonStop2());
|
|
15489
|
+
} else if (sub === "run") {
|
|
15490
|
+
await daemonRunChild2(Number(args[2]));
|
|
15491
|
+
return;
|
|
15492
|
+
} else {
|
|
15493
|
+
process.stdout.write(await daemonStatus2());
|
|
15494
|
+
}
|
|
15495
|
+
} catch (err) {
|
|
15496
|
+
process.stdout.write(`\u{1F43B} Daemon error: ${err}`);
|
|
15497
|
+
}
|
|
15498
|
+
process.exit(0);
|
|
15499
|
+
}
|
|
15151
15500
|
if (args[0] === "--hook") {
|
|
15152
15501
|
process.exit(0);
|
|
15153
15502
|
}
|
|
@@ -15158,8 +15507,8 @@ ${line}
|
|
|
15158
15507
|
const proj = root.split("/").pop() || "unknown";
|
|
15159
15508
|
const lines = [`\u{1F43B} [Kuma session brief] ${proj}`];
|
|
15160
15509
|
try {
|
|
15161
|
-
const { execSync:
|
|
15162
|
-
const branch =
|
|
15510
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
15511
|
+
const branch = execSync10("git rev-parse --abbrev-ref HEAD", {
|
|
15163
15512
|
cwd: root,
|
|
15164
15513
|
encoding: "utf-8",
|
|
15165
15514
|
timeout: 2e3,
|
|
@@ -15277,23 +15626,23 @@ ${line}
|
|
|
15277
15626
|
});
|
|
15278
15627
|
const output = formatInitResults(results);
|
|
15279
15628
|
console.log(output);
|
|
15280
|
-
const
|
|
15281
|
-
const
|
|
15282
|
-
const matchaSkills =
|
|
15283
|
-
const matchaAgents =
|
|
15629
|
+
const fs34 = await import("fs");
|
|
15630
|
+
const path37 = await import("path");
|
|
15631
|
+
const matchaSkills = path37.resolve(process.cwd(), "skills/matcha/SKILL.md");
|
|
15632
|
+
const matchaAgents = path37.resolve(
|
|
15284
15633
|
process.cwd(),
|
|
15285
15634
|
".agents/skills/matcha/SKILL.md"
|
|
15286
15635
|
);
|
|
15287
|
-
const matchaRootSkills =
|
|
15636
|
+
const matchaRootSkills = path37.resolve(
|
|
15288
15637
|
process.cwd(),
|
|
15289
15638
|
"skills/matcha/SKILL.md"
|
|
15290
15639
|
);
|
|
15291
|
-
const matchaAgentsMd =
|
|
15292
|
-
const matchaWindsurfRules =
|
|
15640
|
+
const matchaAgentsMd = path37.resolve(process.cwd(), "AGENTS.md");
|
|
15641
|
+
const matchaWindsurfRules = path37.resolve(
|
|
15293
15642
|
process.cwd(),
|
|
15294
15643
|
".windsurf"
|
|
15295
15644
|
);
|
|
15296
|
-
if (
|
|
15645
|
+
if (fs34.existsSync(matchaSkills) || fs34.existsSync(matchaAgents) || fs34.existsSync(matchaRootSkills) || fs34.existsSync(matchaAgentsMd) || fs34.existsSync(matchaWindsurfRules)) {
|
|
15297
15646
|
console.error(
|
|
15298
15647
|
"\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!"
|
|
15299
15648
|
);
|
|
@@ -15307,13 +15656,13 @@ ${line}
|
|
|
15307
15656
|
(async () => {
|
|
15308
15657
|
try {
|
|
15309
15658
|
const { generateInitMdContent: generateInitMdContent2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
15310
|
-
const
|
|
15311
|
-
const
|
|
15312
|
-
const initMdPath =
|
|
15313
|
-
if (!
|
|
15314
|
-
const kumaDir =
|
|
15315
|
-
if (!
|
|
15316
|
-
|
|
15659
|
+
const fs34 = await import("fs");
|
|
15660
|
+
const path37 = await import("path");
|
|
15661
|
+
const initMdPath = path37.resolve(process.cwd(), ".kuma/init.md");
|
|
15662
|
+
if (!fs34.existsSync(initMdPath)) {
|
|
15663
|
+
const kumaDir = path37.dirname(initMdPath);
|
|
15664
|
+
if (!fs34.existsSync(kumaDir)) fs34.mkdirSync(kumaDir, { recursive: true });
|
|
15665
|
+
fs34.writeFileSync(initMdPath, generateInitMdContent2(), "utf-8");
|
|
15317
15666
|
console.error(`[${SERVER_NAME}] Auto-generated .kuma/init.md`);
|
|
15318
15667
|
}
|
|
15319
15668
|
} catch (err) {
|
|
@@ -15324,8 +15673,8 @@ ${line}
|
|
|
15324
15673
|
try {
|
|
15325
15674
|
const { detectAgent: detectAgent2, getSkillPath: getSkillPath2, getAgentLabel: getAgentLabel2 } = await Promise.resolve().then(() => (init_agentDetector(), agentDetector_exports));
|
|
15326
15675
|
const { generateSkill: generateSkill2, getSecondaryFiles: getSecondaryFiles2 } = await Promise.resolve().then(() => (init_skillGenerator(), skillGenerator_exports));
|
|
15327
|
-
const
|
|
15328
|
-
const
|
|
15676
|
+
const fs34 = await import("fs");
|
|
15677
|
+
const path37 = await import("path");
|
|
15329
15678
|
const detection = detectAgent2();
|
|
15330
15679
|
if (!detection.primary) {
|
|
15331
15680
|
console.error(`[${SERVER_NAME}] No AI agent detected \u2014 skipping auto-skill creation`);
|
|
@@ -15333,22 +15682,22 @@ ${line}
|
|
|
15333
15682
|
}
|
|
15334
15683
|
const agentType = detection.primary;
|
|
15335
15684
|
const skillPath = getSkillPath2(agentType);
|
|
15336
|
-
const fullPath =
|
|
15337
|
-
if (
|
|
15685
|
+
const fullPath = path37.resolve(process.cwd(), skillPath);
|
|
15686
|
+
if (fs34.existsSync(fullPath)) {
|
|
15338
15687
|
console.error(`[${SERVER_NAME}] Skill exists for ${getAgentLabel2(agentType)} \u2014 skipping`);
|
|
15339
15688
|
return;
|
|
15340
15689
|
}
|
|
15341
|
-
const dir =
|
|
15342
|
-
if (!
|
|
15343
|
-
|
|
15690
|
+
const dir = path37.dirname(fullPath);
|
|
15691
|
+
if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
|
|
15692
|
+
fs34.writeFileSync(fullPath, generateSkill2(agentType), "utf-8");
|
|
15344
15693
|
console.error(`[${SERVER_NAME}] Auto-created ${skillPath} for ${getAgentLabel2(agentType)}`);
|
|
15345
15694
|
const secondaryFiles = getSecondaryFiles2(agentType);
|
|
15346
15695
|
for (const sf of secondaryFiles) {
|
|
15347
|
-
const sfPath =
|
|
15348
|
-
const sfDir =
|
|
15349
|
-
if (!
|
|
15350
|
-
if (!
|
|
15351
|
-
|
|
15696
|
+
const sfPath = path37.resolve(process.cwd(), sf.path);
|
|
15697
|
+
const sfDir = path37.dirname(sfPath);
|
|
15698
|
+
if (!fs34.existsSync(sfPath)) {
|
|
15699
|
+
if (!fs34.existsSync(sfDir)) fs34.mkdirSync(sfDir, { recursive: true });
|
|
15700
|
+
fs34.writeFileSync(sfPath, sf.content, "utf-8");
|
|
15352
15701
|
console.error(`[${SERVER_NAME}] Auto-created ${sf.path}`);
|
|
15353
15702
|
}
|
|
15354
15703
|
}
|