@plumpslabs/kuma 2.4.14 → 2.4.16
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 +1125 -526
- 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,326 @@ var init_workspaceIntelligence = __esm({
|
|
|
4577
4589
|
}
|
|
4578
4590
|
});
|
|
4579
4591
|
|
|
4592
|
+
// src/engine/mapBackbone.ts
|
|
4593
|
+
var mapBackbone_exports = {};
|
|
4594
|
+
__export(mapBackbone_exports, {
|
|
4595
|
+
BACKBONE_MAX_FILES: () => BACKBONE_MAX_FILES,
|
|
4596
|
+
buildBackbone: () => buildBackbone,
|
|
4597
|
+
ensureBackbone: () => ensureBackbone,
|
|
4598
|
+
ensureMapped: () => ensureMapped,
|
|
4599
|
+
extractImportSources: () => extractImportSources,
|
|
4600
|
+
formatBackbone: () => formatBackbone,
|
|
4601
|
+
isMapEmpty: () => isMapEmpty,
|
|
4602
|
+
readMapMeta: () => readMapMeta
|
|
4603
|
+
});
|
|
4604
|
+
import fs9 from "fs";
|
|
4605
|
+
import path11 from "path";
|
|
4606
|
+
import fastGlob3 from "fast-glob";
|
|
4607
|
+
function extractImportSources(line) {
|
|
4608
|
+
const out = [];
|
|
4609
|
+
const t = line.trim();
|
|
4610
|
+
if (t.startsWith("//") || t.startsWith("#") || t.startsWith("*")) return out;
|
|
4611
|
+
for (const re of IMPORT_RES) {
|
|
4612
|
+
re.lastIndex = 0;
|
|
4613
|
+
let m;
|
|
4614
|
+
while ((m = re.exec(line)) !== null) {
|
|
4615
|
+
if (m[1] && m[1].startsWith(".")) out.push(m[1]);
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
return out;
|
|
4619
|
+
}
|
|
4620
|
+
function mapMetaPath(root) {
|
|
4621
|
+
return path11.join(root || getProjectRoot(), MAP_META);
|
|
4622
|
+
}
|
|
4623
|
+
function readMapMeta(root) {
|
|
4624
|
+
try {
|
|
4625
|
+
const fp = mapMetaPath(root);
|
|
4626
|
+
if (fs9.existsSync(fp)) return JSON.parse(fs9.readFileSync(fp, "utf-8"));
|
|
4627
|
+
} catch {
|
|
4628
|
+
}
|
|
4629
|
+
return {};
|
|
4630
|
+
}
|
|
4631
|
+
function writeMapMeta(patch, root) {
|
|
4632
|
+
try {
|
|
4633
|
+
const fp = mapMetaPath(root);
|
|
4634
|
+
const prev = readMapMeta(root);
|
|
4635
|
+
fs9.writeFileSync(fp, JSON.stringify({ ...prev, ...patch }, null, 2), "utf-8");
|
|
4636
|
+
} catch {
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
async function isMapEmpty() {
|
|
4640
|
+
try {
|
|
4641
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
4642
|
+
const db = await getDb2();
|
|
4643
|
+
const stmt = db.prepare(`SELECT COUNT(*) as cnt FROM nodes WHERE type = 'file'`);
|
|
4644
|
+
let n = 0;
|
|
4645
|
+
if (stmt.step()) n = Number(stmt.getAsObject().cnt) || 0;
|
|
4646
|
+
stmt.free();
|
|
4647
|
+
return n === 0;
|
|
4648
|
+
} catch {
|
|
4649
|
+
return true;
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
async function buildBackbone(maxFiles = BACKBONE_MAX_FILES) {
|
|
4653
|
+
const started = Date.now();
|
|
4654
|
+
const root = getProjectRoot();
|
|
4655
|
+
const result = { files: 0, capped: false, nodes: 0, edges: 0, ms: 0, filesPerSec: 0 };
|
|
4656
|
+
let files = [];
|
|
4657
|
+
try {
|
|
4658
|
+
files = await fastGlob3([...DEFAULT_SOURCE_INCLUDES], {
|
|
4659
|
+
cwd: root,
|
|
4660
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**", "**/.next/**", "**/coverage/**", "**/*.d.ts", "**/.kuma/**"],
|
|
4661
|
+
onlyFiles: true,
|
|
4662
|
+
deep: 7,
|
|
4663
|
+
dot: false
|
|
4664
|
+
});
|
|
4665
|
+
} catch {
|
|
4666
|
+
return result;
|
|
4667
|
+
}
|
|
4668
|
+
const capped = files.length > maxFiles;
|
|
4669
|
+
const batch = files.slice(0, maxFiles);
|
|
4670
|
+
result.files = batch.length;
|
|
4671
|
+
result.capped = capped;
|
|
4672
|
+
const existingEdges = /* @__PURE__ */ new Set();
|
|
4673
|
+
const existingFiles = /* @__PURE__ */ new Set();
|
|
4674
|
+
try {
|
|
4675
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
4676
|
+
const db = await getDb2();
|
|
4677
|
+
const stmt = db.prepare(`SELECT source_id, target_id FROM edges WHERE type = 'imports'`);
|
|
4678
|
+
while (stmt.step()) {
|
|
4679
|
+
const r = stmt.getAsObject();
|
|
4680
|
+
existingEdges.add(`${r.source_id}\u2192${r.target_id}`);
|
|
4681
|
+
}
|
|
4682
|
+
stmt.free();
|
|
4683
|
+
const fStmt = db.prepare(`SELECT id FROM nodes WHERE type = 'file'`);
|
|
4684
|
+
while (fStmt.step()) existingFiles.add(fStmt.getAsObject().id);
|
|
4685
|
+
fStmt.free();
|
|
4686
|
+
} catch {
|
|
4687
|
+
}
|
|
4688
|
+
for (const filePath of batch) {
|
|
4689
|
+
try {
|
|
4690
|
+
const full = path11.join(root, filePath);
|
|
4691
|
+
const stat = fs9.statSync(full);
|
|
4692
|
+
if (!stat.isFile() || stat.size > 512 * 1024) continue;
|
|
4693
|
+
const fileId = `file::${filePath}`;
|
|
4694
|
+
if (!existingFiles.has(fileId)) {
|
|
4695
|
+
try {
|
|
4696
|
+
await upsertNode({ id: fileId, type: "file", name: filePath, filePath, metadata: { backbone: true } });
|
|
4697
|
+
existingFiles.add(fileId);
|
|
4698
|
+
result.nodes++;
|
|
4699
|
+
} catch {
|
|
4700
|
+
continue;
|
|
4701
|
+
}
|
|
4702
|
+
}
|
|
4703
|
+
const content = fs9.readFileSync(full, "utf-8");
|
|
4704
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4705
|
+
for (const line of content.split("\n")) {
|
|
4706
|
+
for (const src of extractImportSources(line)) {
|
|
4707
|
+
if (seen.has(src)) continue;
|
|
4708
|
+
seen.add(src);
|
|
4709
|
+
try {
|
|
4710
|
+
const resolved = resolveImportPath(filePath, src, root);
|
|
4711
|
+
const key = resolved ? `${fileId}\u2192file::${resolved}` : "";
|
|
4712
|
+
if (resolved && !existingEdges.has(key)) {
|
|
4713
|
+
await addEdge({ sourceId: fileId, targetId: `file::${resolved}`, type: "imports" });
|
|
4714
|
+
existingEdges.add(key);
|
|
4715
|
+
result.edges++;
|
|
4716
|
+
}
|
|
4717
|
+
} catch {
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
} catch {
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
result.ms = Date.now() - started;
|
|
4725
|
+
result.filesPerSec = result.ms > 0 ? Math.round(result.files / result.ms * 1e3) : result.files;
|
|
4726
|
+
writeMapMeta({
|
|
4727
|
+
backboneAt: Date.now(),
|
|
4728
|
+
backboneFiles: result.files,
|
|
4729
|
+
backboneCapped: result.capped,
|
|
4730
|
+
backboneMs: result.ms,
|
|
4731
|
+
filesPerSec: result.filesPerSec
|
|
4732
|
+
});
|
|
4733
|
+
return result;
|
|
4734
|
+
}
|
|
4735
|
+
async function ensureBackbone() {
|
|
4736
|
+
try {
|
|
4737
|
+
if (!await isMapEmpty()) return null;
|
|
4738
|
+
return await buildBackbone();
|
|
4739
|
+
} catch {
|
|
4740
|
+
return null;
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4743
|
+
function formatBackbone(r) {
|
|
4744
|
+
return `\u{1F5FA}\uFE0F Backbone: ${r.files} file(s)${r.capped ? ` (capped at ${BACKBONE_MAX_FILES})` : ""}, ${r.edges} import edge(s) in ${r.ms}ms (~${r.filesPerSec} files/s) \u2014 approximate tier, symbols fill in on demand.`;
|
|
4745
|
+
}
|
|
4746
|
+
async function ensureMapped(files) {
|
|
4747
|
+
const targets = [...new Set(files.map((f) => f.trim()).filter(Boolean))].slice(0, 10);
|
|
4748
|
+
if (targets.length === 0) return { filled: 0 };
|
|
4749
|
+
let missing = [];
|
|
4750
|
+
try {
|
|
4751
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
4752
|
+
const db = await getDb2();
|
|
4753
|
+
for (const f of targets) {
|
|
4754
|
+
const stmt = db.prepare(
|
|
4755
|
+
`SELECT COUNT(*) as cnt FROM nodes WHERE type IN ('function','class','method','interface') AND file_path LIKE ?`
|
|
4756
|
+
);
|
|
4757
|
+
stmt.bind([`%${path11.basename(f)}%`]);
|
|
4758
|
+
let n = 0;
|
|
4759
|
+
if (stmt.step()) n = Number(stmt.getAsObject().cnt) || 0;
|
|
4760
|
+
stmt.free();
|
|
4761
|
+
if (n === 0) missing.push(f);
|
|
4762
|
+
}
|
|
4763
|
+
} catch {
|
|
4764
|
+
missing = targets;
|
|
4765
|
+
}
|
|
4766
|
+
if (missing.length === 0) return { filled: 0 };
|
|
4767
|
+
try {
|
|
4768
|
+
const { scanCodebase: scanCodebase2 } = await Promise.resolve().then(() => (init_kumaCodeScanner(), kumaCodeScanner_exports));
|
|
4769
|
+
const res = await scanCodebase2({ include: missing, force: true, maxFiles: missing.length });
|
|
4770
|
+
if (res.filesScanned > 0) writeMapMeta({ precisionAt: Date.now() });
|
|
4771
|
+
return { filled: Math.min(missing.length, res.filesScanned) };
|
|
4772
|
+
} catch {
|
|
4773
|
+
return { filled: 0 };
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
var BACKBONE_MAX_FILES, MAP_META, IMPORT_RES;
|
|
4777
|
+
var init_mapBackbone = __esm({
|
|
4778
|
+
"src/engine/mapBackbone.ts"() {
|
|
4779
|
+
"use strict";
|
|
4780
|
+
init_pathValidator();
|
|
4781
|
+
init_kumaGraph();
|
|
4782
|
+
init_kumaCodeScanner();
|
|
4783
|
+
init_languageSupport();
|
|
4784
|
+
BACKBONE_MAX_FILES = 2e3;
|
|
4785
|
+
MAP_META = ".kuma/map-meta.json";
|
|
4786
|
+
IMPORT_RES = [
|
|
4787
|
+
/(?:import)\s+(?:[\w*{},\s]+\s+from\s+|[\w$]+\s+)?['"]([^'"]+)['"]/g,
|
|
4788
|
+
/(?:from)\s+["']?([a-zA-Z0-9_./-]+)["']?\s+import\s+/g,
|
|
4789
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g
|
|
4790
|
+
];
|
|
4791
|
+
}
|
|
4792
|
+
});
|
|
4793
|
+
|
|
4794
|
+
// src/engine/symbolReach.ts
|
|
4795
|
+
var symbolReach_exports = {};
|
|
4796
|
+
__export(symbolReach_exports, {
|
|
4797
|
+
formatReachability: () => formatReachability,
|
|
4798
|
+
symbolReachability: () => symbolReachability
|
|
4799
|
+
});
|
|
4800
|
+
import fs10 from "fs";
|
|
4801
|
+
import path12 from "path";
|
|
4802
|
+
import { execSync as execSync4 } from "child_process";
|
|
4803
|
+
async function graphReach(symbol, limit) {
|
|
4804
|
+
const rows = [];
|
|
4805
|
+
try {
|
|
4806
|
+
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
4807
|
+
const db = await getDb2();
|
|
4808
|
+
const symStmt = db.prepare(
|
|
4809
|
+
`SELECT id, file_path FROM nodes WHERE type IN ('function','class','method') AND (name = ? OR name LIKE ?) LIMIT 5`
|
|
4810
|
+
);
|
|
4811
|
+
symStmt.bind([symbol, `%::${symbol}`]);
|
|
4812
|
+
const symIds = [];
|
|
4813
|
+
while (symStmt.step()) {
|
|
4814
|
+
const r = symStmt.getAsObject();
|
|
4815
|
+
symIds.push({ id: r.id, file: r.file_path });
|
|
4816
|
+
}
|
|
4817
|
+
symStmt.free();
|
|
4818
|
+
if (symIds.length === 0) return [];
|
|
4819
|
+
for (const s of symIds) {
|
|
4820
|
+
const cStmt = db.prepare(
|
|
4821
|
+
`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`
|
|
4822
|
+
);
|
|
4823
|
+
cStmt.bind([s.id]);
|
|
4824
|
+
const callers = /* @__PURE__ */ new Set();
|
|
4825
|
+
while (cStmt.step()) {
|
|
4826
|
+
const fp = cStmt.getAsObject().fp;
|
|
4827
|
+
if (fp) callers.add(fp);
|
|
4828
|
+
}
|
|
4829
|
+
cStmt.free();
|
|
4830
|
+
for (const fp of [...callers, s.file]) {
|
|
4831
|
+
const tStmt = db.prepare(
|
|
4832
|
+
`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`
|
|
4833
|
+
);
|
|
4834
|
+
tStmt.bind([`%${fp}%`, `%${s.id}%`]);
|
|
4835
|
+
while (tStmt.step()) {
|
|
4836
|
+
const tfp = tStmt.getAsObject().tfp;
|
|
4837
|
+
if (tfp && !rows.some((r) => r.test === tfp)) {
|
|
4838
|
+
rows.push({ test: tfp, via: fp, evidence: `${tfp} tests ${fp} (graph edge)` });
|
|
4839
|
+
}
|
|
4840
|
+
if (rows.length >= limit) break;
|
|
4841
|
+
}
|
|
4842
|
+
tStmt.free();
|
|
4843
|
+
if (rows.length >= limit) break;
|
|
4844
|
+
}
|
|
4845
|
+
if (rows.length >= limit) break;
|
|
4846
|
+
}
|
|
4847
|
+
} catch {
|
|
4848
|
+
}
|
|
4849
|
+
return rows;
|
|
4850
|
+
}
|
|
4851
|
+
function ripgrepReach(symbol, root, limit) {
|
|
4852
|
+
const rows = [];
|
|
4853
|
+
try {
|
|
4854
|
+
const out = execSync4(
|
|
4855
|
+
`grep -rn -F --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.py" --include="*.go" --include="*.rs" "${symbol.replace(/"/g, "")}" . 2>/dev/null | grep -v node_modules | grep -v "/dist/" | grep -v "/.git/" | grep -v "/.kuma/" | head -40`,
|
|
4856
|
+
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
4857
|
+
).trim();
|
|
4858
|
+
if (!out) return [];
|
|
4859
|
+
const callers = /* @__PURE__ */ new Set();
|
|
4860
|
+
for (const line of out.split("\n")) {
|
|
4861
|
+
const m = line.match(/^([^:]+):(\d+):(.*)$/);
|
|
4862
|
+
if (!m) continue;
|
|
4863
|
+
const [, fp, ln, code] = m;
|
|
4864
|
+
if (new RegExp(`\\b${symbol}\\s*\\(`).test(code)) callers.add(`${fp}:${ln}`);
|
|
4865
|
+
}
|
|
4866
|
+
for (const c of [...callers].slice(0, BERBEDA)) {
|
|
4867
|
+
const [fp] = c.split(":");
|
|
4868
|
+
const dir = path12.dirname(fp);
|
|
4869
|
+
const stem = path12.basename(fp, path12.extname(fp)).split(/[-_.]/)[0];
|
|
4870
|
+
try {
|
|
4871
|
+
const dirFull = path12.join(root, dir);
|
|
4872
|
+
if (!fs10.existsSync(dirFull)) continue;
|
|
4873
|
+
for (const f of fs10.readdirSync(dirFull)) {
|
|
4874
|
+
if (!/(\.test\.|\.spec\.|_test\.)/i.test(f)) continue;
|
|
4875
|
+
const low = f.toLowerCase();
|
|
4876
|
+
if (low.includes(stem.toLowerCase()) || low.includes(symbol.toLowerCase())) {
|
|
4877
|
+
const rel = path12.join(dir, f);
|
|
4878
|
+
if (!rows.some((r) => r.test === rel)) {
|
|
4879
|
+
rows.push({ test: rel, via: fp, evidence: `${rel} sibling-matches caller ${fp} (called at ${c})` });
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
if (rows.length >= limit) break;
|
|
4883
|
+
}
|
|
4884
|
+
} catch {
|
|
4885
|
+
}
|
|
4886
|
+
if (rows.length >= limit) break;
|
|
4887
|
+
}
|
|
4888
|
+
} catch {
|
|
4889
|
+
}
|
|
4890
|
+
return rows;
|
|
4891
|
+
}
|
|
4892
|
+
async function symbolReachability(symbol, limit = 10, root) {
|
|
4893
|
+
const g = await graphReach(symbol, limit);
|
|
4894
|
+
if (g.length > 0) return { tier: 1, rows: g };
|
|
4895
|
+
return { tier: 2, rows: ripgrepReach(symbol, root || getProjectRoot(), limit) };
|
|
4896
|
+
}
|
|
4897
|
+
function formatReachability(symbol, tier, rows) {
|
|
4898
|
+
if (rows.length === 0) return `\u{1F50E} Reachability \`${symbol}\` (tier ${tier}): no tests reach this symbol.`;
|
|
4899
|
+
const lines = [`\u{1F50E} Reachability \`${symbol}\` (tier ${tier}, ${rows.length} test(s)):`];
|
|
4900
|
+
for (const r of rows.slice(0, 5)) lines.push(` \u2022 \`${r.test}\` via \`${r.via}\` \u2014 ${r.evidence}`);
|
|
4901
|
+
return lines.join("\n");
|
|
4902
|
+
}
|
|
4903
|
+
var BERBEDA;
|
|
4904
|
+
var init_symbolReach = __esm({
|
|
4905
|
+
"src/engine/symbolReach.ts"() {
|
|
4906
|
+
"use strict";
|
|
4907
|
+
init_pathValidator();
|
|
4908
|
+
BERBEDA = 10;
|
|
4909
|
+
}
|
|
4910
|
+
});
|
|
4911
|
+
|
|
4580
4912
|
// src/engine/testHistory.ts
|
|
4581
4913
|
var testHistory_exports = {};
|
|
4582
4914
|
__export(testHistory_exports, {
|
|
@@ -4692,17 +5024,17 @@ __export(impactAnalysis_exports, {
|
|
|
4692
5024
|
classifyDiffShape: () => classifyDiffShape,
|
|
4693
5025
|
getIndexerStatus: () => getIndexerStatus
|
|
4694
5026
|
});
|
|
4695
|
-
import
|
|
4696
|
-
import
|
|
4697
|
-
import { execSync as
|
|
4698
|
-
import
|
|
5027
|
+
import fs11 from "fs";
|
|
5028
|
+
import path13 from "path";
|
|
5029
|
+
import { execSync as execSync5 } from "child_process";
|
|
5030
|
+
import fastGlob4 from "fast-glob";
|
|
4699
5031
|
function extOf(target) {
|
|
4700
|
-
const base =
|
|
5032
|
+
const base = path13.basename(target);
|
|
4701
5033
|
const dot = base.lastIndexOf(".");
|
|
4702
5034
|
return dot > 0 ? base.slice(dot).toLowerCase() : "";
|
|
4703
5035
|
}
|
|
4704
5036
|
function isExampleOrDocName(target) {
|
|
4705
|
-
const base =
|
|
5037
|
+
const base = path13.basename(target).toLowerCase();
|
|
4706
5038
|
return base.endsWith(".example") || base.startsWith(".env") || base.endsWith(".md") || base.endsWith(".txt") || base.endsWith(".rst") || base === "license" || base.startsWith("license.");
|
|
4707
5039
|
}
|
|
4708
5040
|
function classifyDiffShape(target, root) {
|
|
@@ -4715,7 +5047,7 @@ function classifyDiffShape(target, root) {
|
|
|
4715
5047
|
}
|
|
4716
5048
|
try {
|
|
4717
5049
|
const cwd = root || getProjectRoot();
|
|
4718
|
-
const diff =
|
|
5050
|
+
const diff = execSync5(`git diff --unified=0 -- "${target}"`, {
|
|
4719
5051
|
cwd,
|
|
4720
5052
|
encoding: "utf-8",
|
|
4721
5053
|
timeout: 4e3,
|
|
@@ -4735,7 +5067,7 @@ function classifyDiffShape(target, root) {
|
|
|
4735
5067
|
return { shape: "runtime-code", detail: "runtime code change" };
|
|
4736
5068
|
}
|
|
4737
5069
|
async function findImportingFiles(targetFile, root) {
|
|
4738
|
-
const normTarget = normalizeScope(
|
|
5070
|
+
const normTarget = normalizeScope(path13.normalize(targetFile)) || path13.normalize(targetFile);
|
|
4739
5071
|
try {
|
|
4740
5072
|
const db = await getDb();
|
|
4741
5073
|
const targetFileId = `file::${normTarget}`;
|
|
@@ -4774,10 +5106,10 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4774
5106
|
}
|
|
4775
5107
|
} catch {
|
|
4776
5108
|
}
|
|
4777
|
-
const baseName =
|
|
4778
|
-
const relTarget =
|
|
5109
|
+
const baseName = path13.basename(normTarget, path13.extname(normTarget));
|
|
5110
|
+
const relTarget = path13.isAbsolute(normTarget) ? path13.relative(root, normTarget) : normTarget;
|
|
4779
5111
|
try {
|
|
4780
|
-
const files = await
|
|
5112
|
+
const files = await fastGlob4(
|
|
4781
5113
|
["**/*.{ts,tsx,js,jsx,mjs,cjs,py,go,rs}"],
|
|
4782
5114
|
{
|
|
4783
5115
|
cwd: root,
|
|
@@ -4788,14 +5120,14 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4788
5120
|
const dependents = [];
|
|
4789
5121
|
const evidence = [];
|
|
4790
5122
|
const importRegex = new RegExp(
|
|
4791
|
-
`(?:import|from|require\\s*\\(
|
|
5123
|
+
`(?:import|from|require\\s*\\()[\\w{}*,.\\s]*['"][^'"]*\\b${baseName}(?:\\.[a-zA-Z0-9]+)?['"]`,
|
|
4792
5124
|
"i"
|
|
4793
5125
|
);
|
|
4794
5126
|
for (const f of files) {
|
|
4795
|
-
if (
|
|
4796
|
-
const fullPath =
|
|
5127
|
+
if (path13.normalize(f) === path13.normalize(relTarget)) continue;
|
|
5128
|
+
const fullPath = path13.join(root, f);
|
|
4797
5129
|
try {
|
|
4798
|
-
const content =
|
|
5130
|
+
const content = fs11.readFileSync(fullPath, "utf-8");
|
|
4799
5131
|
const lines = content.split("\n");
|
|
4800
5132
|
const hitIdx = lines.findIndex((l) => importRegex.test(l));
|
|
4801
5133
|
if (hitIdx >= 0) {
|
|
@@ -4811,7 +5143,7 @@ async function findImportingFiles(targetFile, root) {
|
|
|
4811
5143
|
}
|
|
4812
5144
|
}
|
|
4813
5145
|
async function findRelatedTests(targetFile, root, owningPackage, additionalDependents = [], exactOnly = false) {
|
|
4814
|
-
const normTarget = normalizeScope(
|
|
5146
|
+
const normTarget = normalizeScope(path13.normalize(targetFile)) || path13.normalize(targetFile);
|
|
4815
5147
|
try {
|
|
4816
5148
|
const db = await getDb();
|
|
4817
5149
|
const targetFileId = `file::${normTarget}`;
|
|
@@ -4835,13 +5167,13 @@ async function findRelatedTests(targetFile, root, owningPackage, additionalDepen
|
|
|
4835
5167
|
}
|
|
4836
5168
|
} catch {
|
|
4837
5169
|
}
|
|
4838
|
-
const baseName =
|
|
5170
|
+
const baseName = path13.basename(normTarget, path13.extname(normTarget));
|
|
4839
5171
|
const testFiles = [];
|
|
4840
5172
|
const stems = /* @__PURE__ */ new Set();
|
|
4841
5173
|
if (baseName) stems.add(baseName);
|
|
4842
5174
|
if (!exactOnly) {
|
|
4843
5175
|
for (const dep of additionalDependents.slice(0, 3)) {
|
|
4844
|
-
const depBase =
|
|
5176
|
+
const depBase = path13.basename(dep, path13.extname(dep));
|
|
4845
5177
|
if (depBase) stems.add(depBase);
|
|
4846
5178
|
}
|
|
4847
5179
|
}
|
|
@@ -4857,7 +5189,7 @@ async function findRelatedTests(targetFile, root, owningPackage, additionalDepen
|
|
|
4857
5189
|
);
|
|
4858
5190
|
}
|
|
4859
5191
|
try {
|
|
4860
|
-
const matches = await
|
|
5192
|
+
const matches = await fastGlob4(testPatterns, {
|
|
4861
5193
|
cwd: root,
|
|
4862
5194
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/.kuma/**"],
|
|
4863
5195
|
onlyFiles: true
|
|
@@ -4876,13 +5208,13 @@ async function findRelatedTests(targetFile, root, owningPackage, additionalDepen
|
|
|
4876
5208
|
`**/__tests__/**/*${stem}*.*`
|
|
4877
5209
|
);
|
|
4878
5210
|
}
|
|
4879
|
-
const pkgTests = await
|
|
4880
|
-
cwd:
|
|
5211
|
+
const pkgTests = await fastGlob4(pkgPatterns, {
|
|
5212
|
+
cwd: path13.join(root, owningPackage.path),
|
|
4881
5213
|
ignore: ["**/node_modules/**", "**/dist/**"],
|
|
4882
5214
|
onlyFiles: true
|
|
4883
5215
|
});
|
|
4884
5216
|
for (const pt of pkgTests) {
|
|
4885
|
-
const fullRel =
|
|
5217
|
+
const fullRel = path13.join(owningPackage.path, pt);
|
|
4886
5218
|
if (!testFiles.includes(fullRel)) {
|
|
4887
5219
|
testFiles.push(fullRel);
|
|
4888
5220
|
}
|
|
@@ -4933,18 +5265,29 @@ function assessRisk(target, targetType, directDependents, downstreamPackages, af
|
|
|
4933
5265
|
}
|
|
4934
5266
|
async function getIndexerStatus() {
|
|
4935
5267
|
let graphEdges = false;
|
|
5268
|
+
let backboneOnly = false;
|
|
4936
5269
|
try {
|
|
4937
5270
|
const db = await getDb();
|
|
4938
5271
|
const stmt = db.prepare(`SELECT COUNT(*) as cnt FROM edges WHERE type IN ('imports','depends_on','calls','tests')`);
|
|
4939
5272
|
if (stmt.step()) graphEdges = Number(stmt.getAsObject().cnt) > 0;
|
|
4940
5273
|
stmt.free();
|
|
5274
|
+
const fStmt = db.prepare(`SELECT COUNT(*) as cnt FROM nodes WHERE type = 'file'`);
|
|
5275
|
+
let files = 0;
|
|
5276
|
+
if (fStmt.step()) files = Number(fStmt.getAsObject().cnt) || 0;
|
|
5277
|
+
fStmt.free();
|
|
5278
|
+
const sStmt = db.prepare(`SELECT COUNT(*) as cnt FROM nodes WHERE type IN ('function','class','method','interface')`);
|
|
5279
|
+
let syms = 0;
|
|
5280
|
+
if (sStmt.step()) syms = Number(sStmt.getAsObject().cnt) || 0;
|
|
5281
|
+
sStmt.free();
|
|
5282
|
+
backboneOnly = files > 0 && syms === 0;
|
|
4941
5283
|
} catch {
|
|
4942
5284
|
}
|
|
4943
5285
|
return {
|
|
4944
5286
|
tier: graphEdges ? 1 : 2,
|
|
4945
5287
|
graphEdges,
|
|
5288
|
+
backboneOnly,
|
|
4946
5289
|
languages: ["ts", "tsx", "js", "jsx", "mjs", "cjs", "py", "go", "rs"],
|
|
4947
|
-
note: graphEdges ? "Tier 1: graph edges grounded the verdict (Tier 2 ripgrep fallback available)" : "Tier 2: ripgrep import-regex fallback (no graph edges; run a scan to reach Tier 1; LSP indexers are future work per #35)"
|
|
5290
|
+
note: backboneOnly ? "Backbone-tier coverage: import graph only (approximate) \u2014 symbols fill in on demand when impact/research touches an area" : graphEdges ? "Tier 1: graph edges grounded the verdict (Tier 2 ripgrep fallback available)" : "Tier 2: ripgrep import-regex fallback (no graph edges; run a scan to reach Tier 1; LSP indexers are future work per #35)"
|
|
4948
5291
|
};
|
|
4949
5292
|
}
|
|
4950
5293
|
async function calculateBlastRadius(target, options = {}) {
|
|
@@ -4970,6 +5313,13 @@ async function calculateBlastRadius(target, options = {}) {
|
|
|
4970
5313
|
const rev = getReverseDependencies(owningPackage.name, wsInfo);
|
|
4971
5314
|
downstreamPackages.push(...rev.map((p) => p.name));
|
|
4972
5315
|
}
|
|
5316
|
+
if (targetType === "file") {
|
|
5317
|
+
try {
|
|
5318
|
+
const { ensureMapped: ensureMapped2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
5319
|
+
await ensureMapped2([target]);
|
|
5320
|
+
} catch {
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
4973
5323
|
let directDependents = [];
|
|
4974
5324
|
let evidence = [];
|
|
4975
5325
|
if (targetType === "file" || targetType === "symbol") {
|
|
@@ -4981,6 +5331,20 @@ async function calculateBlastRadius(target, options = {}) {
|
|
|
4981
5331
|
const nonRuntime = diffShape !== "runtime-code";
|
|
4982
5332
|
const affectedTests = await findRelatedTests(target, root, owningPackage, directDependents, nonRuntime);
|
|
4983
5333
|
let testReasons = /* @__PURE__ */ new Map();
|
|
5334
|
+
if (targetType === "symbol") {
|
|
5335
|
+
try {
|
|
5336
|
+
const { symbolReachability: symbolReachability2 } = await Promise.resolve().then(() => (init_symbolReach(), symbolReach_exports));
|
|
5337
|
+
const reach = await symbolReachability2(target, 10, root);
|
|
5338
|
+
for (const r of reach.rows) {
|
|
5339
|
+
if (!affectedTests.includes(r.test)) {
|
|
5340
|
+
affectedTests.push(r.test);
|
|
5341
|
+
testReasons.set(r.test, `reachability tier ${reach.tier}: ${r.evidence.substring(0, 80)}`);
|
|
5342
|
+
}
|
|
5343
|
+
evidence.push(r.evidence);
|
|
5344
|
+
}
|
|
5345
|
+
} catch {
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
4984
5348
|
try {
|
|
4985
5349
|
const { rankTestsByHistory: rankTestsByHistory2 } = await Promise.resolve().then(() => (init_testHistory(), testHistory_exports));
|
|
4986
5350
|
const ranked = await rankTestsByHistory2(target, affectedTests);
|
|
@@ -6195,27 +6559,27 @@ async function codebaseSearchFallback(query, limit = 20) {
|
|
|
6195
6559
|
if (results.length >= limit || depth > MAX_WALK_DEPTH) return;
|
|
6196
6560
|
let entries = [];
|
|
6197
6561
|
try {
|
|
6198
|
-
entries =
|
|
6562
|
+
entries = fs35.readdirSync(dir);
|
|
6199
6563
|
} catch {
|
|
6200
6564
|
return;
|
|
6201
6565
|
}
|
|
6202
6566
|
for (const entry of entries) {
|
|
6203
6567
|
if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "build") continue;
|
|
6204
|
-
const full =
|
|
6568
|
+
const full = path38.join(dir, entry);
|
|
6205
6569
|
try {
|
|
6206
|
-
const stat =
|
|
6570
|
+
const stat = fs35.statSync(full);
|
|
6207
6571
|
if (stat.isDirectory()) {
|
|
6208
6572
|
walk2(full, depth + 1);
|
|
6209
6573
|
} else if (entry.toLowerCase().includes(query.toLowerCase())) {
|
|
6210
|
-
results.push(
|
|
6574
|
+
results.push(path38.relative(root, full));
|
|
6211
6575
|
}
|
|
6212
6576
|
} catch {
|
|
6213
6577
|
}
|
|
6214
6578
|
}
|
|
6215
6579
|
};
|
|
6216
6580
|
var walk = walk2;
|
|
6217
|
-
const
|
|
6218
|
-
const
|
|
6581
|
+
const fs35 = await import("fs");
|
|
6582
|
+
const path38 = await import("path");
|
|
6219
6583
|
const root = process.cwd();
|
|
6220
6584
|
const results = [];
|
|
6221
6585
|
const MAX_WALK_DEPTH = 8;
|
|
@@ -6348,9 +6712,9 @@ ${summary}`;
|
|
|
6348
6712
|
}
|
|
6349
6713
|
async function codebaseImpactFallback(target) {
|
|
6350
6714
|
try {
|
|
6351
|
-
const { execSync:
|
|
6715
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
6352
6716
|
const root = process.cwd();
|
|
6353
|
-
const grepResult =
|
|
6717
|
+
const grepResult = execSync10(
|
|
6354
6718
|
`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
6719
|
{ cwd: root, encoding: "utf-8", timeout: 5e3, maxBuffer: 256 * 1024 }
|
|
6356
6720
|
).trim();
|
|
@@ -6496,9 +6860,9 @@ __export(kumaShared_exports, {
|
|
|
6496
6860
|
isReadTool: () => isReadTool,
|
|
6497
6861
|
isTestTool: () => isTestTool
|
|
6498
6862
|
});
|
|
6499
|
-
import { execSync as
|
|
6500
|
-
import
|
|
6501
|
-
import
|
|
6863
|
+
import { execSync as execSync6 } from "child_process";
|
|
6864
|
+
import fs12 from "fs";
|
|
6865
|
+
import path14 from "path";
|
|
6502
6866
|
function isEditTool(toolName) {
|
|
6503
6867
|
const n = (toolName || "").toLowerCase();
|
|
6504
6868
|
return n.includes("edit") || n.includes("write") || n.includes("patch") || n.includes("apply") || n.includes("replace");
|
|
@@ -6536,7 +6900,7 @@ function getSessionStats(inputGoal) {
|
|
|
6536
6900
|
function getGitDiffStat(timeout = 3e3) {
|
|
6537
6901
|
try {
|
|
6538
6902
|
const root = getProjectRoot();
|
|
6539
|
-
return
|
|
6903
|
+
return execSync6("git diff --stat", {
|
|
6540
6904
|
cwd: root,
|
|
6541
6905
|
encoding: "utf-8",
|
|
6542
6906
|
timeout
|
|
@@ -6549,7 +6913,7 @@ function getUncommittedFiles(timeout = 3e3) {
|
|
|
6549
6913
|
const files = /* @__PURE__ */ new Set();
|
|
6550
6914
|
try {
|
|
6551
6915
|
const root = getProjectRoot();
|
|
6552
|
-
const porcelain =
|
|
6916
|
+
const porcelain = execSync6("git status --porcelain", {
|
|
6553
6917
|
cwd: root,
|
|
6554
6918
|
encoding: "utf-8",
|
|
6555
6919
|
timeout,
|
|
@@ -6568,17 +6932,17 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6568
6932
|
let base = "";
|
|
6569
6933
|
try {
|
|
6570
6934
|
const root = getProjectRoot();
|
|
6571
|
-
const branch =
|
|
6935
|
+
const branch = execSync6("git rev-parse --abbrev-ref HEAD", {
|
|
6572
6936
|
cwd: root,
|
|
6573
6937
|
encoding: "utf-8",
|
|
6574
6938
|
timeout,
|
|
6575
6939
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6576
6940
|
}).trim();
|
|
6577
|
-
const baseRef =
|
|
6941
|
+
const baseRef = execSync6(
|
|
6578
6942
|
`git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || echo origin/${branch === "main" || branch === "master" ? "HEAD" : "main"}`,
|
|
6579
6943
|
{ cwd: root, encoding: "utf-8", timeout, stdio: ["pipe", "pipe", "pipe"] }
|
|
6580
6944
|
).trim().replace("refs/remotes/", "");
|
|
6581
|
-
const mergeBase =
|
|
6945
|
+
const mergeBase = execSync6(`git merge-base HEAD ${baseRef} 2>/dev/null || git rev-list --max-parents=0 HEAD`, {
|
|
6582
6946
|
cwd: root,
|
|
6583
6947
|
encoding: "utf-8",
|
|
6584
6948
|
timeout,
|
|
@@ -6586,7 +6950,7 @@ function getBranchCommittedFiles(timeout = 5e3) {
|
|
|
6586
6950
|
}).trim().split("\n")[0];
|
|
6587
6951
|
base = baseRef;
|
|
6588
6952
|
if (mergeBase) {
|
|
6589
|
-
const out =
|
|
6953
|
+
const out = execSync6(`git diff --name-only ${mergeBase} HEAD`, {
|
|
6590
6954
|
cwd: root,
|
|
6591
6955
|
encoding: "utf-8",
|
|
6592
6956
|
timeout,
|
|
@@ -6604,10 +6968,10 @@ function hasVerificationEvidence(toolCalls) {
|
|
|
6604
6968
|
}
|
|
6605
6969
|
function hasVerificationInHookLog(hours = 24) {
|
|
6606
6970
|
try {
|
|
6607
|
-
const fp =
|
|
6608
|
-
if (!
|
|
6971
|
+
const fp = path14.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
6972
|
+
if (!fs12.existsSync(fp)) return false;
|
|
6609
6973
|
const cutoff = Date.now() - hours * 60 * 60 * 1e3;
|
|
6610
|
-
const lines =
|
|
6974
|
+
const lines = fs12.readFileSync(fp, "utf-8").split("\n");
|
|
6611
6975
|
for (const line of lines) {
|
|
6612
6976
|
if (!line.trim()) continue;
|
|
6613
6977
|
try {
|
|
@@ -6710,7 +7074,7 @@ function detectTestWeakening(files, root) {
|
|
|
6710
7074
|
for (const f of files) {
|
|
6711
7075
|
if (!TEST_FILE_PATTERN.test(f)) continue;
|
|
6712
7076
|
try {
|
|
6713
|
-
const diff =
|
|
7077
|
+
const diff = execSync6(`git diff --unified=0 -- "${f}"`, {
|
|
6714
7078
|
cwd,
|
|
6715
7079
|
encoding: "utf-8",
|
|
6716
7080
|
timeout: 4e3,
|
|
@@ -6749,19 +7113,19 @@ __export(kumaMemory_exports, {
|
|
|
6749
7113
|
scoreMemoryRelevance: () => scoreMemoryRelevance,
|
|
6750
7114
|
shouldRecordDecision: () => shouldRecordDecision
|
|
6751
7115
|
});
|
|
6752
|
-
import
|
|
6753
|
-
import
|
|
7116
|
+
import fs13 from "fs";
|
|
7117
|
+
import path15 from "path";
|
|
6754
7118
|
function scoreMemoryRelevance(context, limit = 5) {
|
|
6755
7119
|
const results = [];
|
|
6756
|
-
const kumaDir =
|
|
6757
|
-
if (!
|
|
7120
|
+
const kumaDir = path15.join(getProjectRoot(), MEMORY_DIR);
|
|
7121
|
+
if (!fs13.existsSync(kumaDir)) return [];
|
|
6758
7122
|
const terms = Array.from(new Set(context.toLowerCase().split(/[\s/._-]+/).filter((w) => w.length > 2)));
|
|
6759
7123
|
if (terms.length === 0) return [];
|
|
6760
7124
|
try {
|
|
6761
|
-
const files =
|
|
7125
|
+
const files = fs13.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
|
|
6762
7126
|
for (const file of files) {
|
|
6763
7127
|
try {
|
|
6764
|
-
const content =
|
|
7128
|
+
const content = fs13.readFileSync(path15.join(kumaDir, file), "utf-8");
|
|
6765
7129
|
const lower = content.toLowerCase();
|
|
6766
7130
|
let matchCount = 0;
|
|
6767
7131
|
for (const term of terms) {
|
|
@@ -6943,8 +7307,8 @@ __export(cacheFreshness_exports, {
|
|
|
6943
7307
|
isStale: () => isStale,
|
|
6944
7308
|
markDirty: () => markDirty
|
|
6945
7309
|
});
|
|
6946
|
-
import
|
|
6947
|
-
import
|
|
7310
|
+
import fs14 from "fs";
|
|
7311
|
+
import path16 from "path";
|
|
6948
7312
|
function isStale(savedAtMs, ttlMs, nowMs = Date.now()) {
|
|
6949
7313
|
if (!savedAtMs) return true;
|
|
6950
7314
|
return nowMs - savedAtMs > ttlMs;
|
|
@@ -6957,13 +7321,13 @@ function formatAge(savedAtMs, nowMs = Date.now()) {
|
|
|
6957
7321
|
return `${Math.floor(s / 86400)}d`;
|
|
6958
7322
|
}
|
|
6959
7323
|
function dirtyPath() {
|
|
6960
|
-
return
|
|
7324
|
+
return path16.join(getProjectRoot(), DIRTY_FILE);
|
|
6961
7325
|
}
|
|
6962
7326
|
function loadDirty() {
|
|
6963
7327
|
try {
|
|
6964
7328
|
const fp = dirtyPath();
|
|
6965
|
-
if (
|
|
6966
|
-
const parsed = JSON.parse(
|
|
7329
|
+
if (fs14.existsSync(fp)) {
|
|
7330
|
+
const parsed = JSON.parse(fs14.readFileSync(fp, "utf-8"));
|
|
6967
7331
|
if (parsed && typeof parsed === "object") return parsed;
|
|
6968
7332
|
}
|
|
6969
7333
|
} catch {
|
|
@@ -6973,14 +7337,14 @@ function loadDirty() {
|
|
|
6973
7337
|
function markDirty(filePath) {
|
|
6974
7338
|
try {
|
|
6975
7339
|
const fp = dirtyPath();
|
|
6976
|
-
const dir =
|
|
6977
|
-
if (!
|
|
7340
|
+
const dir = path16.dirname(fp);
|
|
7341
|
+
if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
|
|
6978
7342
|
const dirty = loadDirty();
|
|
6979
7343
|
dirty[filePath] = Date.now();
|
|
6980
7344
|
const keys = Object.keys(dirty).sort((a, b) => dirty[b] - dirty[a]).slice(0, 200);
|
|
6981
7345
|
const bounded = {};
|
|
6982
7346
|
for (const k of keys) bounded[k] = dirty[k];
|
|
6983
|
-
|
|
7347
|
+
fs14.writeFileSync(fp, JSON.stringify(bounded), "utf-8");
|
|
6984
7348
|
} catch {
|
|
6985
7349
|
}
|
|
6986
7350
|
}
|
|
@@ -6990,7 +7354,7 @@ function getDirtyFiles() {
|
|
|
6990
7354
|
function clearDirty() {
|
|
6991
7355
|
try {
|
|
6992
7356
|
const fp = dirtyPath();
|
|
6993
|
-
if (
|
|
7357
|
+
if (fs14.existsSync(fp)) fs14.rmSync(fp, { force: true });
|
|
6994
7358
|
} catch {
|
|
6995
7359
|
}
|
|
6996
7360
|
}
|
|
@@ -7010,8 +7374,8 @@ var codeReuseEngine_exports = {};
|
|
|
7010
7374
|
__export(codeReuseEngine_exports, {
|
|
7011
7375
|
findReusableSymbols: () => findReusableSymbols
|
|
7012
7376
|
});
|
|
7013
|
-
import
|
|
7014
|
-
import
|
|
7377
|
+
import path17 from "path";
|
|
7378
|
+
import fs15 from "fs";
|
|
7015
7379
|
async function findReusableSymbols(query, scope) {
|
|
7016
7380
|
const cleanQuery = query.trim().toLowerCase();
|
|
7017
7381
|
const searchTokens = cleanQuery.replace(/[^a-zA-Z0-9_\s]/g, " ").split(/\s+/).filter((t) => t.length >= 2);
|
|
@@ -7134,7 +7498,7 @@ function detectRecommendedUtilDirectory() {
|
|
|
7134
7498
|
"utils"
|
|
7135
7499
|
];
|
|
7136
7500
|
for (const c of candidates) {
|
|
7137
|
-
if (
|
|
7501
|
+
if (fs15.existsSync(path17.resolve(root, c))) {
|
|
7138
7502
|
return c;
|
|
7139
7503
|
}
|
|
7140
7504
|
}
|
|
@@ -7153,7 +7517,7 @@ var domainClusterEngine_exports = {};
|
|
|
7153
7517
|
__export(domainClusterEngine_exports, {
|
|
7154
7518
|
buildSubsystemClusters: () => buildSubsystemClusters
|
|
7155
7519
|
});
|
|
7156
|
-
import
|
|
7520
|
+
import path18 from "path";
|
|
7157
7521
|
async function buildSubsystemClusters() {
|
|
7158
7522
|
const db = await getDb();
|
|
7159
7523
|
const fileStmt = db.prepare(`
|
|
@@ -7230,7 +7594,7 @@ async function buildSubsystemClusters() {
|
|
|
7230
7594
|
for (const [cKey, files] of clusterFileMap.entries()) {
|
|
7231
7595
|
for (const f of files) {
|
|
7232
7596
|
fileToCluster.set(f, cKey);
|
|
7233
|
-
fileToCluster.set(
|
|
7597
|
+
fileToCluster.set(path18.normalize(f), cKey);
|
|
7234
7598
|
}
|
|
7235
7599
|
}
|
|
7236
7600
|
const clusterInteractions = /* @__PURE__ */ new Map();
|
|
@@ -7238,8 +7602,8 @@ async function buildSubsystemClusters() {
|
|
|
7238
7602
|
const srcFile = extractFilePathFromNodeId(e.source);
|
|
7239
7603
|
const tgtFile = extractFilePathFromNodeId(e.target);
|
|
7240
7604
|
if (!srcFile || !tgtFile) continue;
|
|
7241
|
-
const srcCluster = fileToCluster.get(srcFile) || fileToCluster.get(
|
|
7242
|
-
const tgtCluster = fileToCluster.get(tgtFile) || fileToCluster.get(
|
|
7605
|
+
const srcCluster = fileToCluster.get(srcFile) || fileToCluster.get(path18.normalize(srcFile));
|
|
7606
|
+
const tgtCluster = fileToCluster.get(tgtFile) || fileToCluster.get(path18.normalize(tgtFile));
|
|
7243
7607
|
if (srcCluster && tgtCluster && srcCluster !== tgtCluster) {
|
|
7244
7608
|
const pairKey = `${srcCluster}->${tgtCluster}`;
|
|
7245
7609
|
const stats = clusterInteractions.get(pairKey) || { calls: 0, imports: 0 };
|
|
@@ -7261,13 +7625,13 @@ async function buildSubsystemClusters() {
|
|
|
7261
7625
|
const clusterId = nodeId("feature_domain", cKey);
|
|
7262
7626
|
const name = formatClusterName(cKey);
|
|
7263
7627
|
const role = inferSubsystemRole(cKey, files);
|
|
7264
|
-
const entrypoints = files.filter((f) => /index|main|server|app|engine|root/i.test(
|
|
7628
|
+
const entrypoints = files.filter((f) => /index|main|server|app|engine|root/i.test(path18.basename(f))).slice(0, 3);
|
|
7265
7629
|
if (entrypoints.length === 0 && files.length > 0) {
|
|
7266
7630
|
entrypoints.push(files[0]);
|
|
7267
7631
|
}
|
|
7268
7632
|
const allSyms = [];
|
|
7269
7633
|
for (const f of files) {
|
|
7270
|
-
const syms = fileSymbols.get(f) || fileSymbols.get(
|
|
7634
|
+
const syms = fileSymbols.get(f) || fileSymbols.get(path18.normalize(f)) || [];
|
|
7271
7635
|
for (const s of syms) {
|
|
7272
7636
|
if (!allSyms.includes(s)) allSyms.push(s);
|
|
7273
7637
|
}
|
|
@@ -7446,7 +7810,7 @@ function inferSubsystemRole(key, files) {
|
|
|
7446
7810
|
if (k.includes("util") || k.includes("common")) {
|
|
7447
7811
|
return "Shared path validation, configuration loading, and platform detection helpers.";
|
|
7448
7812
|
}
|
|
7449
|
-
return `Core subsystem handling ${files.slice(0, 3).map((f) =>
|
|
7813
|
+
return `Core subsystem handling ${files.slice(0, 3).map((f) => path18.basename(f)).join(", ")}.`;
|
|
7450
7814
|
}
|
|
7451
7815
|
function inferTypedVerb(sourceKey, targetKey, _stats) {
|
|
7452
7816
|
const src = sourceKey.toLowerCase();
|
|
@@ -7525,18 +7889,18 @@ __export(domainRules_exports, {
|
|
|
7525
7889
|
readLayer: () => readLayer,
|
|
7526
7890
|
writeLayer: () => writeLayer
|
|
7527
7891
|
});
|
|
7528
|
-
import
|
|
7529
|
-
import
|
|
7892
|
+
import fs16 from "fs";
|
|
7893
|
+
import path19 from "path";
|
|
7530
7894
|
function layerFilePath(layer) {
|
|
7531
|
-
return
|
|
7895
|
+
return path19.join(getProjectRoot(), ".kuma", LAYER_FILES[layer].filename);
|
|
7532
7896
|
}
|
|
7533
7897
|
function ensureLayerFile(layer) {
|
|
7534
7898
|
const fp = layerFilePath(layer);
|
|
7535
|
-
if (!
|
|
7899
|
+
if (!fs16.existsSync(fp)) {
|
|
7536
7900
|
const template = layer === "arch_flow" ? ARCH_FLOW_TEMPLATE : GOTCHAS_TEMPLATE;
|
|
7537
|
-
const dir =
|
|
7538
|
-
if (!
|
|
7539
|
-
|
|
7901
|
+
const dir = path19.dirname(fp);
|
|
7902
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
7903
|
+
fs16.writeFileSync(fp, template, "utf-8");
|
|
7540
7904
|
console.error(`[DomainRules] Auto-created ${LAYER_FILES[layer].filename}`);
|
|
7541
7905
|
}
|
|
7542
7906
|
return fp;
|
|
@@ -7544,7 +7908,7 @@ function ensureLayerFile(layer) {
|
|
|
7544
7908
|
function readLayer(layer) {
|
|
7545
7909
|
try {
|
|
7546
7910
|
const fp = ensureLayerFile(layer);
|
|
7547
|
-
return
|
|
7911
|
+
return fs16.readFileSync(fp, "utf-8");
|
|
7548
7912
|
} catch (err) {
|
|
7549
7913
|
return `Error reading ${layer}: ${err}`;
|
|
7550
7914
|
}
|
|
@@ -7552,7 +7916,7 @@ function readLayer(layer) {
|
|
|
7552
7916
|
function writeLayer(layer, content) {
|
|
7553
7917
|
try {
|
|
7554
7918
|
const fp = ensureLayerFile(layer);
|
|
7555
|
-
|
|
7919
|
+
fs16.writeFileSync(fp, content, "utf-8");
|
|
7556
7920
|
const label = LAYER_FILES[layer].label;
|
|
7557
7921
|
sessionMemory.recordToolCall("kuma_memory", { action: `write_${layer}` });
|
|
7558
7922
|
return `\u2705 **${label}** updated successfully.`;
|
|
@@ -7563,9 +7927,9 @@ function writeLayer(layer, content) {
|
|
|
7563
7927
|
function appendToLayer(layer, entry) {
|
|
7564
7928
|
try {
|
|
7565
7929
|
const fp = ensureLayerFile(layer);
|
|
7566
|
-
const existing =
|
|
7930
|
+
const existing = fs16.readFileSync(fp, "utf-8");
|
|
7567
7931
|
const newContent = existing.trimEnd() + "\n\n" + entry.trim() + "\n";
|
|
7568
|
-
|
|
7932
|
+
fs16.writeFileSync(fp, newContent, "utf-8");
|
|
7569
7933
|
return `\u2705 Entry added to **${LAYER_FILES[layer].label}**.`;
|
|
7570
7934
|
} catch (err) {
|
|
7571
7935
|
return `\u274C Failed to append: ${err}`;
|
|
@@ -7575,7 +7939,7 @@ function getActiveGotchas() {
|
|
|
7575
7939
|
const results = [];
|
|
7576
7940
|
try {
|
|
7577
7941
|
const fp = ensureLayerFile("gotcha");
|
|
7578
|
-
const content =
|
|
7942
|
+
const content = fs16.readFileSync(fp, "utf-8");
|
|
7579
7943
|
const lines = content.split("\n");
|
|
7580
7944
|
let currentSection = "";
|
|
7581
7945
|
let currentFilePath = "";
|
|
@@ -7611,9 +7975,9 @@ function getActiveGotchas() {
|
|
|
7611
7975
|
function checkFileGotchas(filePath) {
|
|
7612
7976
|
const gotchas = getActiveGotchas();
|
|
7613
7977
|
const warnings = [];
|
|
7614
|
-
const base =
|
|
7978
|
+
const base = path19.basename(filePath);
|
|
7615
7979
|
for (const g of gotchas) {
|
|
7616
|
-
const gBase =
|
|
7980
|
+
const gBase = path19.basename(g.filePath);
|
|
7617
7981
|
if (filePath.includes(g.filePath) || g.filePath.includes(filePath) || base && gBase && base === gBase) {
|
|
7618
7982
|
const icon = g.severity === "critical" ? "\u{1F534}" : g.severity === "high" ? "\u{1F7E0}" : g.severity === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
7619
7983
|
warnings.push(`${icon} **Gotcha**: ${g.description} (${g.severity})`);
|
|
@@ -7713,8 +8077,8 @@ __export(kumaInject_exports, {
|
|
|
7713
8077
|
parseHookInput: () => parseHookInput,
|
|
7714
8078
|
trackFileEditLoop: () => trackFileEditLoop
|
|
7715
8079
|
});
|
|
7716
|
-
import
|
|
7717
|
-
import
|
|
8080
|
+
import fs17 from "fs";
|
|
8081
|
+
import path20 from "path";
|
|
7718
8082
|
function hashFileContent(filePath) {
|
|
7719
8083
|
return hashFile(filePath);
|
|
7720
8084
|
}
|
|
@@ -7725,10 +8089,10 @@ function isGotchaFresh(storedHash, currentHash) {
|
|
|
7725
8089
|
}
|
|
7726
8090
|
async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false) {
|
|
7727
8091
|
const root = getProjectRoot();
|
|
7728
|
-
const base =
|
|
8092
|
+
const base = path20.basename(filePath);
|
|
7729
8093
|
try {
|
|
7730
|
-
const dbFile =
|
|
7731
|
-
if (
|
|
8094
|
+
const dbFile = path20.join(root, ".kuma", "kuma.db");
|
|
8095
|
+
if (fs17.existsSync(dbFile)) {
|
|
7732
8096
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7733
8097
|
const db = await getDb2();
|
|
7734
8098
|
const stmt = db.prepare(`
|
|
@@ -7769,11 +8133,11 @@ async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false)
|
|
|
7769
8133
|
} catch {
|
|
7770
8134
|
}
|
|
7771
8135
|
try {
|
|
7772
|
-
if (!
|
|
8136
|
+
if (!fs17.existsSync(path20.join(root, ".kuma"))) return [];
|
|
7773
8137
|
const { getActiveGotchas: getActiveGotchas2 } = await Promise.resolve().then(() => (init_domainRules(), domainRules_exports));
|
|
7774
8138
|
const allGotchas = getActiveGotchas2();
|
|
7775
8139
|
const matched = allGotchas.filter(
|
|
7776
|
-
(g) => g.filePath.includes(filePath) || filePath.includes(g.filePath) || base &&
|
|
8140
|
+
(g) => g.filePath.includes(filePath) || filePath.includes(g.filePath) || base && path20.basename(g.filePath) === base
|
|
7777
8141
|
);
|
|
7778
8142
|
if (matched.length > 0) {
|
|
7779
8143
|
return matched.slice(0, limit).map((g) => ({
|
|
@@ -7792,8 +8156,8 @@ async function getFreshGotchasForFile(filePath, limit = 5, includeStale = false)
|
|
|
7792
8156
|
async function getFileTrace(filePath, limit = 8) {
|
|
7793
8157
|
try {
|
|
7794
8158
|
const root = getProjectRoot();
|
|
7795
|
-
const dbFile =
|
|
7796
|
-
if (!
|
|
8159
|
+
const dbFile = path20.join(root, ".kuma", "kuma.db");
|
|
8160
|
+
if (!fs17.existsSync(dbFile)) return [];
|
|
7797
8161
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7798
8162
|
const db = await getDb2();
|
|
7799
8163
|
const stmt = db.prepare(`
|
|
@@ -7803,7 +8167,7 @@ async function getFileTrace(filePath, limit = 8) {
|
|
|
7803
8167
|
ORDER BY cl.created_at DESC
|
|
7804
8168
|
LIMIT ?
|
|
7805
8169
|
`);
|
|
7806
|
-
stmt.bind([`%${
|
|
8170
|
+
stmt.bind([`%${path20.basename(filePath)}%`, limit]);
|
|
7807
8171
|
const rows = [];
|
|
7808
8172
|
while (stmt.step()) rows.push(stmt.getAsObject());
|
|
7809
8173
|
stmt.free();
|
|
@@ -7821,7 +8185,7 @@ async function getFileTrace(filePath, limit = 8) {
|
|
|
7821
8185
|
function formatFileTrace(entries, filePath) {
|
|
7822
8186
|
if (entries.length === 0) return "";
|
|
7823
8187
|
const lines = [
|
|
7824
|
-
`\u{1F570}\uFE0F **Why is ${
|
|
8188
|
+
`\u{1F570}\uFE0F **Why is ${path20.basename(filePath)} written this way?** \u2014 ${entries.length} change(s) recorded:`
|
|
7825
8189
|
];
|
|
7826
8190
|
for (const e of entries) {
|
|
7827
8191
|
const date = new Date((e.createdAt || 0) * 1e3).toLocaleDateString("en-US", {
|
|
@@ -7837,12 +8201,12 @@ function formatFileTrace(entries, filePath) {
|
|
|
7837
8201
|
async function getDecisionsForFile(filePath, limit = 3) {
|
|
7838
8202
|
const decisions = [];
|
|
7839
8203
|
const root = getProjectRoot();
|
|
7840
|
-
const base =
|
|
8204
|
+
const base = path20.basename(filePath);
|
|
7841
8205
|
const baseWithoutExt = base.replace(/\.[^.]+$/, "");
|
|
7842
8206
|
try {
|
|
7843
|
-
const decisionsMdPath =
|
|
7844
|
-
if (
|
|
7845
|
-
const content =
|
|
8207
|
+
const decisionsMdPath = path20.join(root, ".kuma", "memories", "decisions.md");
|
|
8208
|
+
if (fs17.existsSync(decisionsMdPath)) {
|
|
8209
|
+
const content = fs17.readFileSync(decisionsMdPath, "utf-8");
|
|
7846
8210
|
const sections = content.split(/^##\s+/m);
|
|
7847
8211
|
for (const section of sections) {
|
|
7848
8212
|
if (!section.trim() || section.startsWith("# ")) continue;
|
|
@@ -7947,13 +8311,13 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7947
8311
|
if (traceStr) parts.push(traceStr);
|
|
7948
8312
|
try {
|
|
7949
8313
|
const root = getProjectRoot();
|
|
7950
|
-
if (
|
|
8314
|
+
if (fs17.existsSync(path20.join(root, ".kuma", "kuma.db"))) {
|
|
7951
8315
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7952
8316
|
const db = await getDb2();
|
|
7953
8317
|
const stmt = db.prepare(
|
|
7954
8318
|
`SELECT COUNT(*) FROM known_gotchas WHERE status = 'resolved' AND file_path LIKE ?`
|
|
7955
8319
|
);
|
|
7956
|
-
stmt.bind([`%${
|
|
8320
|
+
stmt.bind([`%${path20.basename(filePath)}%`]);
|
|
7957
8321
|
const found = stmt.step();
|
|
7958
8322
|
const count = found ? Number(stmt.getAsObject()["COUNT(*)"]) : 0;
|
|
7959
8323
|
stmt.free();
|
|
@@ -7964,7 +8328,7 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7964
8328
|
} catch {
|
|
7965
8329
|
}
|
|
7966
8330
|
if (gotchas.length > 0) {
|
|
7967
|
-
const scopeHint =
|
|
8331
|
+
const scopeHint = path20.basename(path20.dirname(filePath)) || "current change";
|
|
7968
8332
|
parts.push(
|
|
7969
8333
|
`\u2705 AFTER editing, verify: kuma_safety({ action: "verify", scope: "${scopeHint}" })`
|
|
7970
8334
|
);
|
|
@@ -7980,11 +8344,11 @@ async function getRelevantContext(filePath, goal) {
|
|
|
7980
8344
|
async function getFileSummary2(filePath) {
|
|
7981
8345
|
try {
|
|
7982
8346
|
const root = getProjectRoot();
|
|
7983
|
-
const dbFile =
|
|
7984
|
-
if (!
|
|
8347
|
+
const dbFile = path20.join(root, ".kuma", "kuma.db");
|
|
8348
|
+
if (!fs17.existsSync(dbFile)) return null;
|
|
7985
8349
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
7986
8350
|
const db = await getDb2();
|
|
7987
|
-
const base =
|
|
8351
|
+
const base = path20.basename(filePath);
|
|
7988
8352
|
const stmt = db.prepare(`
|
|
7989
8353
|
SELECT
|
|
7990
8354
|
(SELECT COUNT(*) FROM known_gotchas WHERE status = 'active' AND (file_path = ? OR file_path LIKE ?)) as active_gotchas,
|
|
@@ -8018,9 +8382,9 @@ function parseHookInput(raw) {
|
|
|
8018
8382
|
const root = data && typeof data === "object" ? data : {};
|
|
8019
8383
|
const ti = root.tool_input && typeof root.tool_input === "object" ? root.tool_input : {};
|
|
8020
8384
|
const args = root.args && typeof root.args === "object" ? root.args : {};
|
|
8021
|
-
const
|
|
8385
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
8022
8386
|
const add = (p) => {
|
|
8023
|
-
if (typeof p === "string" && p.trim())
|
|
8387
|
+
if (typeof p === "string" && p.trim()) paths2.add(p.trim());
|
|
8024
8388
|
};
|
|
8025
8389
|
add(ti.file_path);
|
|
8026
8390
|
add(ti.new_file_path);
|
|
@@ -8038,7 +8402,7 @@ function parseHookInput(raw) {
|
|
|
8038
8402
|
add(args.file);
|
|
8039
8403
|
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
8404
|
const goal = (typeof ti.goal === "string" ? ti.goal : void 0) ?? (typeof root.goal === "string" ? root.goal : void 0);
|
|
8041
|
-
const out = { filePaths: [...
|
|
8405
|
+
const out = { filePaths: [...paths2] };
|
|
8042
8406
|
if (goal) out.goal = goal;
|
|
8043
8407
|
if (cmd) out.command = cmd;
|
|
8044
8408
|
return out;
|
|
@@ -8076,9 +8440,9 @@ async function getCommandContext(command) {
|
|
|
8076
8440
|
}
|
|
8077
8441
|
function loadHookState(root) {
|
|
8078
8442
|
try {
|
|
8079
|
-
const fp =
|
|
8080
|
-
if (
|
|
8081
|
-
return JSON.parse(
|
|
8443
|
+
const fp = path20.join(root, HOOK_STATE_FILE);
|
|
8444
|
+
if (fs17.existsSync(fp)) {
|
|
8445
|
+
return JSON.parse(fs17.readFileSync(fp, "utf-8"));
|
|
8082
8446
|
}
|
|
8083
8447
|
} catch {
|
|
8084
8448
|
}
|
|
@@ -8086,10 +8450,10 @@ function loadHookState(root) {
|
|
|
8086
8450
|
}
|
|
8087
8451
|
function saveHookState(root, state) {
|
|
8088
8452
|
try {
|
|
8089
|
-
const fp =
|
|
8090
|
-
const dir =
|
|
8091
|
-
if (!
|
|
8092
|
-
|
|
8453
|
+
const fp = path20.join(root, HOOK_STATE_FILE);
|
|
8454
|
+
const dir = path20.dirname(fp);
|
|
8455
|
+
if (!fs17.existsSync(dir)) fs17.mkdirSync(dir, { recursive: true });
|
|
8456
|
+
fs17.writeFileSync(fp, JSON.stringify(state), "utf-8");
|
|
8093
8457
|
} catch {
|
|
8094
8458
|
}
|
|
8095
8459
|
}
|
|
@@ -8123,7 +8487,7 @@ async function trackFileEditLoop(filePath) {
|
|
|
8123
8487
|
saveHookState(root, state);
|
|
8124
8488
|
if (recorded) {
|
|
8125
8489
|
try {
|
|
8126
|
-
if (!
|
|
8490
|
+
if (!fs17.existsSync(path20.join(root, ".kuma", "kuma.db"))) return entry.count;
|
|
8127
8491
|
const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
8128
8492
|
await addGotcha2({
|
|
8129
8493
|
filePath,
|
|
@@ -8177,8 +8541,8 @@ __export(kumaGotchas_exports, {
|
|
|
8177
8541
|
syncGotchasToDb: () => syncGotchasToDb,
|
|
8178
8542
|
tokenizeForSimilarity: () => tokenizeForSimilarity
|
|
8179
8543
|
});
|
|
8180
|
-
import
|
|
8181
|
-
import
|
|
8544
|
+
import fs18 from "fs";
|
|
8545
|
+
import path21 from "path";
|
|
8182
8546
|
async function ensureGotchasSchema() {
|
|
8183
8547
|
const db = await getDb();
|
|
8184
8548
|
db.exec(`
|
|
@@ -8600,8 +8964,8 @@ async function getActiveGotchasForCommand(command) {
|
|
|
8600
8964
|
}
|
|
8601
8965
|
async function recordInjection(params) {
|
|
8602
8966
|
try {
|
|
8603
|
-
const fp =
|
|
8604
|
-
if (!
|
|
8967
|
+
const fp = path21.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
8968
|
+
if (!fs18.existsSync(path21.dirname(fp))) fs18.mkdirSync(path21.dirname(fp), { recursive: true });
|
|
8605
8969
|
const line = JSON.stringify({
|
|
8606
8970
|
filePath: params.filePath || null,
|
|
8607
8971
|
command: params.command || null,
|
|
@@ -8610,16 +8974,16 @@ async function recordInjection(params) {
|
|
|
8610
8974
|
// conservative estimate: avoided re-research / re-discovery
|
|
8611
8975
|
at: Date.now()
|
|
8612
8976
|
});
|
|
8613
|
-
|
|
8977
|
+
fs18.appendFileSync(fp, line + "\n", "utf-8");
|
|
8614
8978
|
} catch {
|
|
8615
8979
|
}
|
|
8616
8980
|
}
|
|
8617
8981
|
function getInjectionStats(hours = 24) {
|
|
8618
8982
|
try {
|
|
8619
|
-
const fp =
|
|
8620
|
-
if (!
|
|
8983
|
+
const fp = path21.join(getProjectRoot(), ".kuma", "injections.jsonl");
|
|
8984
|
+
if (!fs18.existsSync(fp)) return { count: 0, savedMs: 0 };
|
|
8621
8985
|
const cutoff = Date.now() - hours * 60 * 60 * 1e3;
|
|
8622
|
-
const lines =
|
|
8986
|
+
const lines = fs18.readFileSync(fp, "utf-8").split("\n");
|
|
8623
8987
|
let count = 0;
|
|
8624
8988
|
let savedMs = 0;
|
|
8625
8989
|
for (const line of lines) {
|
|
@@ -8677,9 +9041,9 @@ async function getGotchasByStatus(status, limit = 20) {
|
|
|
8677
9041
|
}
|
|
8678
9042
|
function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
8679
9043
|
try {
|
|
8680
|
-
const mdPath =
|
|
8681
|
-
if (!
|
|
8682
|
-
const content =
|
|
9044
|
+
const mdPath = path21.join(getProjectRoot(), ".kuma", "KNOWN_GOTCHAS.md");
|
|
9045
|
+
if (!fs18.existsSync(mdPath)) return false;
|
|
9046
|
+
const content = fs18.readFileSync(mdPath, "utf-8");
|
|
8683
9047
|
const lines = content.split("\n");
|
|
8684
9048
|
const normalizedTargets = targetPaths.map((p) => p.trim().toLowerCase()).filter(Boolean);
|
|
8685
9049
|
if (normalizedTargets.length === 0) return false;
|
|
@@ -8696,9 +9060,9 @@ function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
|
8696
9060
|
modified = true;
|
|
8697
9061
|
}
|
|
8698
9062
|
const headingFile = headingMatch[1].trim().toLowerCase();
|
|
8699
|
-
const headingBase =
|
|
9063
|
+
const headingBase = path21.basename(headingFile);
|
|
8700
9064
|
inMatchingSection = normalizedTargets.some(
|
|
8701
|
-
(t) => headingFile.includes(t) || t.includes(headingFile) || headingBase === t || headingBase ===
|
|
9065
|
+
(t) => headingFile.includes(t) || t.includes(headingFile) || headingBase === t || headingBase === path21.basename(t)
|
|
8702
9066
|
);
|
|
8703
9067
|
sectionHasStatus = false;
|
|
8704
9068
|
outputLines.push(line);
|
|
@@ -8720,7 +9084,7 @@ function syncGotchaStatusToMarkdown(targetPaths, newStatus, reason) {
|
|
|
8720
9084
|
modified = true;
|
|
8721
9085
|
}
|
|
8722
9086
|
if (modified) {
|
|
8723
|
-
|
|
9087
|
+
fs18.writeFileSync(mdPath, outputLines.join("\n"), "utf-8");
|
|
8724
9088
|
}
|
|
8725
9089
|
return modified;
|
|
8726
9090
|
} catch {
|
|
@@ -8861,24 +9225,24 @@ var kumaMaintenance_exports = {};
|
|
|
8861
9225
|
__export(kumaMaintenance_exports, {
|
|
8862
9226
|
maybeRunAutoCleanup: () => maybeRunAutoCleanup
|
|
8863
9227
|
});
|
|
8864
|
-
import
|
|
8865
|
-
import
|
|
9228
|
+
import fs19 from "fs";
|
|
9229
|
+
import path22 from "path";
|
|
8866
9230
|
async function maybeRunAutoCleanup() {
|
|
8867
9231
|
try {
|
|
8868
9232
|
const root = getProjectRoot();
|
|
8869
|
-
const stateFile =
|
|
9233
|
+
const stateFile = path22.join(root, GC_STATE_FILE);
|
|
8870
9234
|
const now = Date.now();
|
|
8871
9235
|
let lastRun = 0;
|
|
8872
|
-
if (
|
|
9236
|
+
if (fs19.existsSync(stateFile)) {
|
|
8873
9237
|
try {
|
|
8874
|
-
lastRun = JSON.parse(
|
|
9238
|
+
lastRun = JSON.parse(fs19.readFileSync(stateFile, "utf-8")).lastRun || 0;
|
|
8875
9239
|
} catch {
|
|
8876
9240
|
lastRun = 0;
|
|
8877
9241
|
}
|
|
8878
9242
|
}
|
|
8879
9243
|
if (now - lastRun < GC_INTERVAL_MS) return "";
|
|
8880
|
-
|
|
8881
|
-
|
|
9244
|
+
fs19.mkdirSync(path22.dirname(stateFile), { recursive: true });
|
|
9245
|
+
fs19.writeFileSync(stateFile, JSON.stringify({ lastRun: now }), "utf-8");
|
|
8882
9246
|
const { syncGotchasToDb: syncGotchasToDb2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
|
|
8883
9247
|
await syncGotchasToDb2();
|
|
8884
9248
|
const { runGarbageCollection: runGarbageCollection2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
@@ -8914,22 +9278,22 @@ __export(kumaPathRules_exports, {
|
|
|
8914
9278
|
loadPathRules: () => loadPathRules,
|
|
8915
9279
|
matchRulePath: () => matchRulePath
|
|
8916
9280
|
});
|
|
8917
|
-
import
|
|
8918
|
-
import
|
|
9281
|
+
import fs20 from "fs";
|
|
9282
|
+
import path23 from "path";
|
|
8919
9283
|
function rulesDir() {
|
|
8920
|
-
return
|
|
9284
|
+
return path23.join(getProjectRoot(), RULES_DIR);
|
|
8921
9285
|
}
|
|
8922
9286
|
function ensureRulesDir() {
|
|
8923
9287
|
try {
|
|
8924
9288
|
const dir = rulesDir();
|
|
8925
|
-
if (!
|
|
9289
|
+
if (!fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
8926
9290
|
} catch {
|
|
8927
9291
|
}
|
|
8928
9292
|
}
|
|
8929
9293
|
function parseRuleFile(filePath) {
|
|
8930
9294
|
try {
|
|
8931
|
-
const raw =
|
|
8932
|
-
const id =
|
|
9295
|
+
const raw = fs20.readFileSync(filePath, "utf-8");
|
|
9296
|
+
const id = path23.basename(filePath, ".md");
|
|
8933
9297
|
const fm = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
8934
9298
|
if (!fm) {
|
|
8935
9299
|
return { id, description: id, paths: ["*"], content: raw.trim(), filePath };
|
|
@@ -8937,15 +9301,15 @@ function parseRuleFile(filePath) {
|
|
|
8937
9301
|
const meta = fm[1];
|
|
8938
9302
|
const body = fm[2].trim();
|
|
8939
9303
|
const description = meta.match(/description:\s*(.+)/)?.[1]?.trim() || id;
|
|
8940
|
-
const
|
|
9304
|
+
const paths2 = [];
|
|
8941
9305
|
const pathSection = meta.match(/paths:\s*\r?\n([\s\S]*?)(?=\r?\n\S|$)/);
|
|
8942
9306
|
if (pathSection) {
|
|
8943
9307
|
for (const line of pathSection[1].split("\n")) {
|
|
8944
9308
|
const m = line.match(/^\s*-\s*['"]?([^'"]+)['"]?\s*$/);
|
|
8945
|
-
if (m)
|
|
9309
|
+
if (m) paths2.push(m[1].trim());
|
|
8946
9310
|
}
|
|
8947
9311
|
}
|
|
8948
|
-
return { id, description, paths:
|
|
9312
|
+
return { id, description, paths: paths2.length ? paths2 : ["*"], content: body, filePath };
|
|
8949
9313
|
} catch {
|
|
8950
9314
|
return null;
|
|
8951
9315
|
}
|
|
@@ -8954,7 +9318,7 @@ function loadPathRules() {
|
|
|
8954
9318
|
ensureRulesDir();
|
|
8955
9319
|
try {
|
|
8956
9320
|
const dir = rulesDir();
|
|
8957
|
-
return
|
|
9321
|
+
return fs20.readdirSync(dir).filter((f) => f.endsWith(".md")).map((f) => parseRuleFile(path23.join(dir, f))).filter((r) => r !== null);
|
|
8958
9322
|
} catch {
|
|
8959
9323
|
return [];
|
|
8960
9324
|
}
|
|
@@ -9042,8 +9406,8 @@ __export(kumaFlowCache_exports, {
|
|
|
9042
9406
|
getFreshDomainFlow: () => getFreshDomainFlow,
|
|
9043
9407
|
getHopsForDomain: () => getHopsForDomain
|
|
9044
9408
|
});
|
|
9045
|
-
import
|
|
9046
|
-
import
|
|
9409
|
+
import fs21 from "fs";
|
|
9410
|
+
import path24 from "path";
|
|
9047
9411
|
async function getFlowFreshness(domain) {
|
|
9048
9412
|
try {
|
|
9049
9413
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
@@ -9078,26 +9442,26 @@ async function getFlowFreshness(domain) {
|
|
|
9078
9442
|
}
|
|
9079
9443
|
function resolveImport(fromFile, spec) {
|
|
9080
9444
|
const root = getProjectRoot();
|
|
9081
|
-
const fromDir =
|
|
9445
|
+
const fromDir = path24.dirname(fromFile);
|
|
9082
9446
|
const candidates = [];
|
|
9083
9447
|
if (spec.startsWith(".")) {
|
|
9084
|
-
const abs =
|
|
9448
|
+
const abs = path24.resolve(fromDir, spec);
|
|
9085
9449
|
candidates.push(abs);
|
|
9086
9450
|
} else {
|
|
9087
|
-
candidates.push(
|
|
9451
|
+
candidates.push(path24.join(root, spec));
|
|
9088
9452
|
}
|
|
9089
9453
|
for (const c of candidates) {
|
|
9090
9454
|
for (const ext of SOURCE_EXTENSIONS) {
|
|
9091
9455
|
const withExt = `${c}${ext}`;
|
|
9092
|
-
if (
|
|
9093
|
-
if (
|
|
9094
|
-
const idx =
|
|
9095
|
-
if (
|
|
9456
|
+
if (fs21.existsSync(withExt)) return path24.relative(root, withExt);
|
|
9457
|
+
if (fs21.existsSync(c) && fs21.statSync(c).isDirectory()) {
|
|
9458
|
+
const idx = path24.join(c, "index") + ext;
|
|
9459
|
+
if (fs21.existsSync(idx)) return path24.relative(root, idx);
|
|
9096
9460
|
}
|
|
9097
9461
|
}
|
|
9098
|
-
if (
|
|
9462
|
+
if (fs21.existsSync(c)) {
|
|
9099
9463
|
try {
|
|
9100
|
-
if (
|
|
9464
|
+
if (fs21.statSync(c).isFile()) return path24.relative(root, c);
|
|
9101
9465
|
} catch {
|
|
9102
9466
|
}
|
|
9103
9467
|
}
|
|
@@ -9106,13 +9470,13 @@ function resolveImport(fromFile, spec) {
|
|
|
9106
9470
|
}
|
|
9107
9471
|
function deriveHopsFromImports(entryFile, maxHops = 6) {
|
|
9108
9472
|
const root = getProjectRoot();
|
|
9109
|
-
const absEntry =
|
|
9110
|
-
if (!
|
|
9473
|
+
const absEntry = path24.isAbsolute(entryFile) ? entryFile : path24.resolve(root, entryFile);
|
|
9474
|
+
if (!fs21.existsSync(absEntry)) return [];
|
|
9111
9475
|
const hops = [];
|
|
9112
9476
|
const seen = /* @__PURE__ */ new Set([entryFile]);
|
|
9113
|
-
const fromName =
|
|
9477
|
+
const fromName = path24.basename(absEntry);
|
|
9114
9478
|
try {
|
|
9115
|
-
const lines =
|
|
9479
|
+
const lines = fs21.readFileSync(absEntry, "utf-8").split("\n");
|
|
9116
9480
|
for (const line of lines) {
|
|
9117
9481
|
if (hops.length >= maxHops) break;
|
|
9118
9482
|
const spec = matchImportPath(line);
|
|
@@ -9122,9 +9486,9 @@ function deriveHopsFromImports(entryFile, maxHops = 6) {
|
|
|
9122
9486
|
seen.add(resolved);
|
|
9123
9487
|
hops.push({
|
|
9124
9488
|
from: fromName,
|
|
9125
|
-
to:
|
|
9489
|
+
to: path24.basename(resolved),
|
|
9126
9490
|
relation: "imports",
|
|
9127
|
-
description: `${fromName} imports ${
|
|
9491
|
+
description: `${fromName} imports ${path24.basename(resolved)}`
|
|
9128
9492
|
});
|
|
9129
9493
|
}
|
|
9130
9494
|
} catch {
|
|
@@ -9187,9 +9551,9 @@ async function getHopsForDomain(domain) {
|
|
|
9187
9551
|
}
|
|
9188
9552
|
try {
|
|
9189
9553
|
const root = getProjectRoot();
|
|
9190
|
-
const archPath =
|
|
9191
|
-
if (
|
|
9192
|
-
const content =
|
|
9554
|
+
const archPath = path24.join(root, ".kuma", "ARCHITECTURE_FLOW.md");
|
|
9555
|
+
if (fs21.existsSync(archPath)) {
|
|
9556
|
+
const content = fs21.readFileSync(archPath, "utf-8");
|
|
9193
9557
|
const lines = content.split("\n");
|
|
9194
9558
|
for (const line of lines) {
|
|
9195
9559
|
const domMatch = line.match(new RegExp(`domain:\\s*${escapeRegex(domain)}\\s*\\|\\s*hops:\\s*(.+)`, "i"));
|
|
@@ -9234,7 +9598,7 @@ Record one with kuma_memory({ action: 'arch_flow', content: 'domain: ${domain} |
|
|
|
9234
9598
|
hops: derivedHops,
|
|
9235
9599
|
filePaths: freshness.filePaths
|
|
9236
9600
|
});
|
|
9237
|
-
lines.push(`\u2705 Re-derived ${derivedHops.length} hop(s) from ${
|
|
9601
|
+
lines.push(`\u2705 Re-derived ${derivedHops.length} hop(s) from ${path24.basename(entry)} \u2014 cache refreshed.`);
|
|
9238
9602
|
hops.splice(0, hops.length, ...derivedHops);
|
|
9239
9603
|
} else {
|
|
9240
9604
|
lines.push("\u26A0\uFE0F No imports to follow \u2014 cache left as-is (record hops manually if needed).");
|
|
@@ -9287,10 +9651,10 @@ __export(packageBriefs_exports, {
|
|
|
9287
9651
|
readBrief: () => readBrief,
|
|
9288
9652
|
refreshPackageBriefs: () => refreshPackageBriefs
|
|
9289
9653
|
});
|
|
9290
|
-
import
|
|
9291
|
-
import
|
|
9654
|
+
import fs22 from "fs";
|
|
9655
|
+
import path25 from "path";
|
|
9292
9656
|
function briefsDir(root) {
|
|
9293
|
-
return
|
|
9657
|
+
return path25.join(root || getProjectRoot(), BRIEFS_DIR);
|
|
9294
9658
|
}
|
|
9295
9659
|
function slug(name) {
|
|
9296
9660
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 60) || "root";
|
|
@@ -9354,13 +9718,13 @@ async function refreshPackageBriefs() {
|
|
|
9354
9718
|
const dir = briefsDir(root);
|
|
9355
9719
|
try {
|
|
9356
9720
|
const ws = await getWorkspaceInfo(root);
|
|
9357
|
-
if (!
|
|
9721
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
9358
9722
|
let count = 0;
|
|
9359
9723
|
const pkgs = ws.isWorkspace ? ws.packages : [{ name: "root", path: ".", isRoot: true }];
|
|
9360
9724
|
for (const p of pkgs.slice(0, 20)) {
|
|
9361
9725
|
const pkg = p;
|
|
9362
9726
|
const content = await buildPackageBrief(pkg.name, pkg.path, root);
|
|
9363
|
-
|
|
9727
|
+
fs22.writeFileSync(path25.join(dir, `${slug(pkg.name)}.md`), content, "utf-8");
|
|
9364
9728
|
count++;
|
|
9365
9729
|
}
|
|
9366
9730
|
return { count, dir };
|
|
@@ -9370,9 +9734,9 @@ async function refreshPackageBriefs() {
|
|
|
9370
9734
|
}
|
|
9371
9735
|
function briefFreshness(pkgName) {
|
|
9372
9736
|
try {
|
|
9373
|
-
const fp =
|
|
9374
|
-
if (!
|
|
9375
|
-
const ageMs = Date.now() -
|
|
9737
|
+
const fp = path25.join(briefsDir(), `${slug(pkgName)}.md`);
|
|
9738
|
+
if (!fs22.existsSync(fp)) return { exists: false, fresh: false, age: "missing" };
|
|
9739
|
+
const ageMs = Date.now() - fs22.statSync(fp).mtimeMs;
|
|
9376
9740
|
return { exists: true, fresh: !isStale(Date.now() - ageMs, MAP_TTL_MS), age: formatAge(Date.now() - ageMs) };
|
|
9377
9741
|
} catch {
|
|
9378
9742
|
return { exists: false, fresh: false, age: "missing" };
|
|
@@ -9380,9 +9744,9 @@ function briefFreshness(pkgName) {
|
|
|
9380
9744
|
}
|
|
9381
9745
|
function readBrief(pkgName, maxChars = 1200) {
|
|
9382
9746
|
try {
|
|
9383
|
-
const fp =
|
|
9384
|
-
if (!
|
|
9385
|
-
const content =
|
|
9747
|
+
const fp = path25.join(briefsDir(), `${slug(pkgName)}.md`);
|
|
9748
|
+
if (!fs22.existsSync(fp)) return null;
|
|
9749
|
+
const content = fs22.readFileSync(fp, "utf-8");
|
|
9386
9750
|
return content.length > maxChars ? content.slice(0, maxChars) + "\n\u2026(truncated)" : content;
|
|
9387
9751
|
} catch {
|
|
9388
9752
|
return null;
|
|
@@ -9411,8 +9775,8 @@ __export(kumaSearch_exports, {
|
|
|
9411
9775
|
formatHybridResults: () => formatHybridResults,
|
|
9412
9776
|
hybridSearch: () => hybridSearch
|
|
9413
9777
|
});
|
|
9414
|
-
import
|
|
9415
|
-
import
|
|
9778
|
+
import fs24 from "fs";
|
|
9779
|
+
import path27 from "path";
|
|
9416
9780
|
function expandQueryTerms(query) {
|
|
9417
9781
|
const terms = query.toLowerCase().split(/[\s_-]+/).filter((t) => t.length > 2);
|
|
9418
9782
|
const expanded = /* @__PURE__ */ new Map();
|
|
@@ -9475,13 +9839,13 @@ async function buildSearchVectors() {
|
|
|
9475
9839
|
}
|
|
9476
9840
|
try {
|
|
9477
9841
|
const root = getProjectRoot();
|
|
9478
|
-
const memDir =
|
|
9479
|
-
if (
|
|
9480
|
-
const files =
|
|
9842
|
+
const memDir = path27.join(root, ".kuma", "memories");
|
|
9843
|
+
if (fs24.existsSync(memDir)) {
|
|
9844
|
+
const files = fs24.readdirSync(memDir).filter((f) => f.endsWith(".md")).slice(0, SCALE_LIMITS.maxMemoryFiles);
|
|
9481
9845
|
for (const file of files) {
|
|
9482
9846
|
try {
|
|
9483
|
-
const fullPath =
|
|
9484
|
-
const content =
|
|
9847
|
+
const fullPath = path27.join(memDir, file);
|
|
9848
|
+
const content = fs24.readFileSync(fullPath, "utf-8");
|
|
9485
9849
|
const text = content.toLowerCase();
|
|
9486
9850
|
const rawTerms = extractTerms(text);
|
|
9487
9851
|
const termCounts = /* @__PURE__ */ new Map();
|
|
@@ -9492,7 +9856,7 @@ async function buildSearchVectors() {
|
|
|
9492
9856
|
terms: termCounts,
|
|
9493
9857
|
source: file.replace(/\.md$/, ""),
|
|
9494
9858
|
sourceType: "memory",
|
|
9495
|
-
recency: recencyScore(Math.floor(
|
|
9859
|
+
recency: recencyScore(Math.floor(fs24.statSync(fullPath).mtimeMs / 1e3))
|
|
9496
9860
|
});
|
|
9497
9861
|
} catch {
|
|
9498
9862
|
}
|
|
@@ -9809,14 +10173,14 @@ __export(architectureGuard_exports, {
|
|
|
9809
10173
|
detectWorkspaceCircularDeps: () => detectWorkspaceCircularDeps,
|
|
9810
10174
|
loadArchitectureRules: () => loadArchitectureRules
|
|
9811
10175
|
});
|
|
9812
|
-
import
|
|
9813
|
-
import
|
|
9814
|
-
import
|
|
10176
|
+
import fs28 from "fs";
|
|
10177
|
+
import path31 from "path";
|
|
10178
|
+
import fastGlob5 from "fast-glob";
|
|
9815
10179
|
function loadArchitectureRules(root) {
|
|
9816
|
-
const rulesPath =
|
|
9817
|
-
if (
|
|
10180
|
+
const rulesPath = path31.join(root, ".kuma", "architecture.json");
|
|
10181
|
+
if (fs28.existsSync(rulesPath)) {
|
|
9818
10182
|
try {
|
|
9819
|
-
const data = JSON.parse(
|
|
10183
|
+
const data = JSON.parse(fs28.readFileSync(rulesPath, "utf-8"));
|
|
9820
10184
|
if (Array.isArray(data.rules)) {
|
|
9821
10185
|
return data.rules;
|
|
9822
10186
|
}
|
|
@@ -9869,7 +10233,7 @@ async function checkArchitectureBoundaries(filesToCheck, customRoot) {
|
|
|
9869
10233
|
}
|
|
9870
10234
|
let targetFiles = filesToCheck;
|
|
9871
10235
|
if (!targetFiles || targetFiles.length === 0) {
|
|
9872
|
-
targetFiles = await
|
|
10236
|
+
targetFiles = await fastGlob5(["**/*.{ts,tsx,js,jsx,mjs,cjs}"], {
|
|
9873
10237
|
cwd: root,
|
|
9874
10238
|
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**", "**/.kuma/**"],
|
|
9875
10239
|
onlyFiles: true
|
|
@@ -9878,11 +10242,11 @@ async function checkArchitectureBoundaries(filesToCheck, customRoot) {
|
|
|
9878
10242
|
const customRules = loadArchitectureRules(root);
|
|
9879
10243
|
const importRegex = /(?:import|export\s+.*from|require\s*\()\s*['"]([^'"]+)['"]/g;
|
|
9880
10244
|
for (const file of targetFiles) {
|
|
9881
|
-
const fullPath =
|
|
9882
|
-
if (!
|
|
10245
|
+
const fullPath = path31.isAbsolute(file) ? file : path31.join(root, file);
|
|
10246
|
+
if (!fs28.existsSync(fullPath)) continue;
|
|
9883
10247
|
let content = "";
|
|
9884
10248
|
try {
|
|
9885
|
-
content =
|
|
10249
|
+
content = fs28.readFileSync(fullPath, "utf-8");
|
|
9886
10250
|
} catch {
|
|
9887
10251
|
continue;
|
|
9888
10252
|
}
|
|
@@ -9892,7 +10256,7 @@ async function checkArchitectureBoundaries(filesToCheck, customRoot) {
|
|
|
9892
10256
|
const importPath = match[1];
|
|
9893
10257
|
if (wsInfo.isWorkspace && currentPkg && !currentPkg.isRoot) {
|
|
9894
10258
|
if (importPath.startsWith("../") && (importPath.includes("/src/") || importPath.includes("/dist/"))) {
|
|
9895
|
-
const resolvedTarget =
|
|
10259
|
+
const resolvedTarget = path31.normalize(path31.resolve(path31.dirname(fullPath), importPath));
|
|
9896
10260
|
const targetPkg = findPackageForFile(resolvedTarget, wsInfo);
|
|
9897
10261
|
if (targetPkg && targetPkg.name !== currentPkg.name) {
|
|
9898
10262
|
violations.push({
|
|
@@ -9941,26 +10305,26 @@ __export(docDrift_exports, {
|
|
|
9941
10305
|
formatDocDrift: () => formatDocDrift,
|
|
9942
10306
|
scanDocDrift: () => scanDocDrift
|
|
9943
10307
|
});
|
|
9944
|
-
import
|
|
9945
|
-
import
|
|
9946
|
-
import { execSync as
|
|
10308
|
+
import fs29 from "fs";
|
|
10309
|
+
import path32 from "path";
|
|
10310
|
+
import { execSync as execSync9 } from "child_process";
|
|
9947
10311
|
function collectDocFiles(root) {
|
|
9948
10312
|
const cwd = root || getProjectRoot();
|
|
9949
10313
|
const out = [];
|
|
9950
10314
|
const pushIfMd = (dir, rel) => {
|
|
9951
10315
|
try {
|
|
9952
|
-
for (const f of
|
|
10316
|
+
for (const f of fs29.readdirSync(dir)) {
|
|
9953
10317
|
if (out.length >= MAX_DOCS) return;
|
|
9954
|
-
const full =
|
|
10318
|
+
const full = path32.join(dir, f);
|
|
9955
10319
|
try {
|
|
9956
|
-
if (
|
|
10320
|
+
if (fs29.statSync(full).isFile() && /\.md$/i.test(f)) out.push(rel(f));
|
|
9957
10321
|
} catch {
|
|
9958
10322
|
}
|
|
9959
10323
|
}
|
|
9960
10324
|
} catch {
|
|
9961
10325
|
}
|
|
9962
10326
|
};
|
|
9963
|
-
pushIfMd(
|
|
10327
|
+
pushIfMd(path32.join(cwd, "docs"), (f) => path32.join("docs", f));
|
|
9964
10328
|
pushIfMd(cwd, (f) => f);
|
|
9965
10329
|
return out;
|
|
9966
10330
|
}
|
|
@@ -10002,7 +10366,7 @@ function codeContainsBatch(root, needles) {
|
|
|
10002
10366
|
const chunk = clean.slice(i, i + 50);
|
|
10003
10367
|
try {
|
|
10004
10368
|
const args = chunk.map((n) => `-e "${n}"`).join(" ");
|
|
10005
|
-
const out =
|
|
10369
|
+
const out = execSync9(
|
|
10006
10370
|
`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
10371
|
{ cwd: root, encoding: "utf-8", timeout: 8e3, maxBuffer: 256 * 1024 }
|
|
10008
10372
|
).trim();
|
|
@@ -10022,7 +10386,7 @@ function scanDocDrift(root) {
|
|
|
10022
10386
|
let claims = [];
|
|
10023
10387
|
for (const rel of collectDocFiles(cwd)) {
|
|
10024
10388
|
try {
|
|
10025
|
-
const content =
|
|
10389
|
+
const content = fs29.readFileSync(path32.join(cwd, rel), "utf-8");
|
|
10026
10390
|
if (isExemptDoc(rel, content)) {
|
|
10027
10391
|
skipped.push(rel);
|
|
10028
10392
|
continue;
|
|
@@ -10193,27 +10557,27 @@ __export(kumaCheckpoint_exports, {
|
|
|
10193
10557
|
pruneCheckpoints: () => pruneCheckpoints,
|
|
10194
10558
|
rollbackToCheckpoint: () => rollbackToCheckpoint
|
|
10195
10559
|
});
|
|
10196
|
-
import
|
|
10197
|
-
import
|
|
10560
|
+
import fs30 from "fs";
|
|
10561
|
+
import path33 from "path";
|
|
10198
10562
|
async function createCheckpoint(label, description) {
|
|
10199
10563
|
try {
|
|
10200
10564
|
const root = getProjectRoot();
|
|
10201
|
-
const cpDir =
|
|
10202
|
-
if (
|
|
10565
|
+
const cpDir = path33.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
10566
|
+
if (fs30.existsSync(cpDir)) {
|
|
10203
10567
|
return `\u26A0\uFE0F Checkpoint "${label}" already exists. Use a different label or remove it first.`;
|
|
10204
10568
|
}
|
|
10205
|
-
|
|
10569
|
+
fs30.mkdirSync(cpDir, { recursive: true });
|
|
10206
10570
|
let gitHead;
|
|
10207
10571
|
let isGitClean = false;
|
|
10208
10572
|
try {
|
|
10209
|
-
const { execSync:
|
|
10210
|
-
gitHead =
|
|
10573
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10574
|
+
gitHead = execSync10("git rev-parse HEAD", {
|
|
10211
10575
|
cwd: root,
|
|
10212
10576
|
encoding: "utf-8",
|
|
10213
10577
|
timeout: 3e3,
|
|
10214
10578
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10215
10579
|
}).trim();
|
|
10216
|
-
const statusOut =
|
|
10580
|
+
const statusOut = execSync10("git status --porcelain", {
|
|
10217
10581
|
cwd: root,
|
|
10218
10582
|
encoding: "utf-8",
|
|
10219
10583
|
timeout: 3e3,
|
|
@@ -10224,8 +10588,8 @@ async function createCheckpoint(label, description) {
|
|
|
10224
10588
|
}
|
|
10225
10589
|
const candidatePaths = /* @__PURE__ */ new Set();
|
|
10226
10590
|
try {
|
|
10227
|
-
const { execSync:
|
|
10228
|
-
const gitOut =
|
|
10591
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10592
|
+
const gitOut = execSync10("git status --porcelain", {
|
|
10229
10593
|
cwd: root,
|
|
10230
10594
|
encoding: "utf-8",
|
|
10231
10595
|
timeout: 4e3,
|
|
@@ -10268,15 +10632,15 @@ async function createCheckpoint(label, description) {
|
|
|
10268
10632
|
}
|
|
10269
10633
|
const files = [];
|
|
10270
10634
|
for (const fp of candidatePaths) {
|
|
10271
|
-
const fullPath =
|
|
10635
|
+
const fullPath = path33.resolve(root, fp);
|
|
10272
10636
|
try {
|
|
10273
|
-
if (
|
|
10274
|
-
const content =
|
|
10637
|
+
if (fs30.existsSync(fullPath) && fs30.statSync(fullPath).isFile()) {
|
|
10638
|
+
const content = fs30.readFileSync(fullPath, "utf-8");
|
|
10275
10639
|
const hash = simpleHash2(content);
|
|
10276
|
-
const snapFile =
|
|
10277
|
-
const fileDir =
|
|
10278
|
-
if (!
|
|
10279
|
-
|
|
10640
|
+
const snapFile = path33.join(cpDir, "files", `${fp}.kuma-snap`);
|
|
10641
|
+
const fileDir = path33.dirname(snapFile);
|
|
10642
|
+
if (!fs30.existsSync(fileDir)) fs30.mkdirSync(fileDir, { recursive: true });
|
|
10643
|
+
fs30.writeFileSync(snapFile, content, "utf-8");
|
|
10280
10644
|
files.push({ path: fp, hash });
|
|
10281
10645
|
}
|
|
10282
10646
|
} catch {
|
|
@@ -10284,7 +10648,7 @@ async function createCheckpoint(label, description) {
|
|
|
10284
10648
|
}
|
|
10285
10649
|
const db = await getDb();
|
|
10286
10650
|
const dbData = db.export();
|
|
10287
|
-
|
|
10651
|
+
fs30.writeFileSync(path33.join(cpDir, "kuma.db"), Buffer.from(dbData));
|
|
10288
10652
|
const manifest = {
|
|
10289
10653
|
label,
|
|
10290
10654
|
timestamp: Date.now(),
|
|
@@ -10294,8 +10658,8 @@ async function createCheckpoint(label, description) {
|
|
|
10294
10658
|
dbSnapshot: true,
|
|
10295
10659
|
description
|
|
10296
10660
|
};
|
|
10297
|
-
|
|
10298
|
-
|
|
10661
|
+
fs30.writeFileSync(
|
|
10662
|
+
path33.join(cpDir, "manifest.json"),
|
|
10299
10663
|
JSON.stringify(manifest, null, 2),
|
|
10300
10664
|
"utf-8"
|
|
10301
10665
|
);
|
|
@@ -10323,29 +10687,29 @@ async function createCheckpoint(label, description) {
|
|
|
10323
10687
|
async function rollbackToCheckpoint(label) {
|
|
10324
10688
|
try {
|
|
10325
10689
|
const root = getProjectRoot();
|
|
10326
|
-
const cpDir =
|
|
10327
|
-
const manifestPath =
|
|
10328
|
-
if (!
|
|
10690
|
+
const cpDir = path33.join(root, CHECKPOINT_DIR, sanitizeLabel(label));
|
|
10691
|
+
const manifestPath = path33.join(cpDir, "manifest.json");
|
|
10692
|
+
if (!fs30.existsSync(manifestPath)) {
|
|
10329
10693
|
return `\u274C Checkpoint "${label}" not found. Available checkpoints:
|
|
10330
10694
|
|
|
10331
10695
|
${listCheckpoints()}`;
|
|
10332
10696
|
}
|
|
10333
10697
|
const manifest = JSON.parse(
|
|
10334
|
-
|
|
10698
|
+
fs30.readFileSync(manifestPath, "utf-8")
|
|
10335
10699
|
);
|
|
10336
10700
|
let restored = 0;
|
|
10337
10701
|
let failed = 0;
|
|
10338
10702
|
for (const f of manifest.files) {
|
|
10339
|
-
const snapPath =
|
|
10340
|
-
const legacyPath =
|
|
10341
|
-
const sourcePath =
|
|
10342
|
-
if (
|
|
10703
|
+
const snapPath = path33.join(cpDir, "files", `${f.path}.kuma-snap`);
|
|
10704
|
+
const legacyPath = path33.join(cpDir, "files", f.path);
|
|
10705
|
+
const sourcePath = fs30.existsSync(snapPath) ? snapPath : legacyPath;
|
|
10706
|
+
if (fs30.existsSync(sourcePath)) {
|
|
10343
10707
|
try {
|
|
10344
|
-
const content =
|
|
10345
|
-
const targetPath =
|
|
10346
|
-
const targetDir =
|
|
10347
|
-
if (!
|
|
10348
|
-
|
|
10708
|
+
const content = fs30.readFileSync(sourcePath, "utf-8");
|
|
10709
|
+
const targetPath = path33.resolve(root, f.path);
|
|
10710
|
+
const targetDir = path33.dirname(targetPath);
|
|
10711
|
+
if (!fs30.existsSync(targetDir)) fs30.mkdirSync(targetDir, { recursive: true });
|
|
10712
|
+
fs30.writeFileSync(targetPath, content, "utf-8");
|
|
10349
10713
|
restored++;
|
|
10350
10714
|
} catch {
|
|
10351
10715
|
failed++;
|
|
@@ -10354,8 +10718,8 @@ ${listCheckpoints()}`;
|
|
|
10354
10718
|
}
|
|
10355
10719
|
if (manifest.files.length === 0 && manifest.gitHead) {
|
|
10356
10720
|
try {
|
|
10357
|
-
const { execSync:
|
|
10358
|
-
|
|
10721
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
10722
|
+
execSync10(`git checkout ${manifest.gitHead} -- .`, {
|
|
10359
10723
|
cwd: root,
|
|
10360
10724
|
encoding: "utf-8",
|
|
10361
10725
|
timeout: 8e3,
|
|
@@ -10366,12 +10730,12 @@ ${listCheckpoints()}`;
|
|
|
10366
10730
|
failed++;
|
|
10367
10731
|
}
|
|
10368
10732
|
}
|
|
10369
|
-
const dbSnapshotPath =
|
|
10370
|
-
if (
|
|
10371
|
-
const kumaDir =
|
|
10372
|
-
const dbPath =
|
|
10373
|
-
const snapshotData =
|
|
10374
|
-
|
|
10733
|
+
const dbSnapshotPath = path33.join(cpDir, "kuma.db");
|
|
10734
|
+
if (fs30.existsSync(dbSnapshotPath)) {
|
|
10735
|
+
const kumaDir = path33.join(root, ".kuma");
|
|
10736
|
+
const dbPath = path33.join(kumaDir, "kuma.db");
|
|
10737
|
+
const snapshotData = fs30.readFileSync(dbSnapshotPath);
|
|
10738
|
+
fs30.writeFileSync(dbPath, snapshotData);
|
|
10375
10739
|
resetDbInstance();
|
|
10376
10740
|
}
|
|
10377
10741
|
sessionMemory.recordToolCall("kuma_checkpoint_rollback", {
|
|
@@ -10396,18 +10760,18 @@ ${listCheckpoints()}`;
|
|
|
10396
10760
|
function listCheckpoints() {
|
|
10397
10761
|
try {
|
|
10398
10762
|
const root = getProjectRoot();
|
|
10399
|
-
const cpDir =
|
|
10400
|
-
if (!
|
|
10763
|
+
const cpDir = path33.join(root, CHECKPOINT_DIR);
|
|
10764
|
+
if (!fs30.existsSync(cpDir)) {
|
|
10401
10765
|
return "\u{1F4ED} No checkpoints found. Use kuma_safety({ action: 'checkpoint', label: 'pre-feature-x' }) to create one.";
|
|
10402
10766
|
}
|
|
10403
|
-
const entries =
|
|
10767
|
+
const entries = fs30.readdirSync(cpDir);
|
|
10404
10768
|
const checkpoints = [];
|
|
10405
10769
|
for (const entry of entries) {
|
|
10406
|
-
const manifestPath =
|
|
10407
|
-
if (
|
|
10770
|
+
const manifestPath = path33.join(cpDir, entry, "manifest.json");
|
|
10771
|
+
if (fs30.existsSync(manifestPath)) {
|
|
10408
10772
|
try {
|
|
10409
10773
|
const manifest = JSON.parse(
|
|
10410
|
-
|
|
10774
|
+
fs30.readFileSync(manifestPath, "utf-8")
|
|
10411
10775
|
);
|
|
10412
10776
|
checkpoints.push({ label: entry, manifest });
|
|
10413
10777
|
} catch {
|
|
@@ -10439,12 +10803,12 @@ function listCheckpoints() {
|
|
|
10439
10803
|
function pruneCheckpoints(keep = 5) {
|
|
10440
10804
|
try {
|
|
10441
10805
|
const root = getProjectRoot();
|
|
10442
|
-
const cpDir =
|
|
10443
|
-
if (!
|
|
10444
|
-
const entries =
|
|
10806
|
+
const cpDir = path33.join(root, CHECKPOINT_DIR);
|
|
10807
|
+
if (!fs30.existsSync(cpDir)) return "\u{1F4ED} No checkpoints to prune.";
|
|
10808
|
+
const entries = fs30.readdirSync(cpDir).map((e) => ({ name: e, time: fs30.statSync(path33.join(cpDir, e)).mtimeMs })).sort((a, b) => b.time - a.time);
|
|
10445
10809
|
let removed = 0;
|
|
10446
10810
|
for (let i = keep; i < entries.length; i++) {
|
|
10447
|
-
|
|
10811
|
+
fs30.rmSync(path33.join(cpDir, entries[i].name), { recursive: true, force: true });
|
|
10448
10812
|
removed++;
|
|
10449
10813
|
}
|
|
10450
10814
|
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 +10833,9 @@ function isRiskyEditPath(filePath) {
|
|
|
10469
10833
|
}
|
|
10470
10834
|
function loadAutoCpDone(root) {
|
|
10471
10835
|
try {
|
|
10472
|
-
const fp =
|
|
10473
|
-
if (
|
|
10474
|
-
const parsed = JSON.parse(
|
|
10836
|
+
const fp = path33.join(root, AUTO_CP_STATE);
|
|
10837
|
+
if (fs30.existsSync(fp)) {
|
|
10838
|
+
const parsed = JSON.parse(fs30.readFileSync(fp, "utf-8"));
|
|
10475
10839
|
return parsed?.autoCheckpoints ?? {};
|
|
10476
10840
|
}
|
|
10477
10841
|
} catch {
|
|
@@ -10480,10 +10844,10 @@ function loadAutoCpDone(root) {
|
|
|
10480
10844
|
}
|
|
10481
10845
|
function saveAutoCpDone(root, done) {
|
|
10482
10846
|
try {
|
|
10483
|
-
const fp =
|
|
10484
|
-
const parsed =
|
|
10847
|
+
const fp = path33.join(root, AUTO_CP_STATE);
|
|
10848
|
+
const parsed = fs30.existsSync(fp) ? JSON.parse(fs30.readFileSync(fp, "utf-8")) : {};
|
|
10485
10849
|
parsed.autoCheckpoints = done;
|
|
10486
|
-
|
|
10850
|
+
fs30.writeFileSync(fp, JSON.stringify(parsed), "utf-8");
|
|
10487
10851
|
} catch {
|
|
10488
10852
|
}
|
|
10489
10853
|
}
|
|
@@ -10493,14 +10857,14 @@ function maybeAutoCheckpoint(filePath) {
|
|
|
10493
10857
|
const root = getProjectRoot();
|
|
10494
10858
|
const done = loadAutoCpDone(root);
|
|
10495
10859
|
if (done[filePath]) return null;
|
|
10496
|
-
const abs =
|
|
10497
|
-
if (!
|
|
10498
|
-
const label = `auto-pre-${sanitizeLabel(
|
|
10499
|
-
const cpDir =
|
|
10500
|
-
if (!
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10860
|
+
const abs = path33.isAbsolute(filePath) ? filePath : path33.join(root, filePath);
|
|
10861
|
+
if (!fs30.existsSync(abs) || !fs30.statSync(abs).isFile()) return null;
|
|
10862
|
+
const label = `auto-pre-${sanitizeLabel(path33.basename(filePath))}`;
|
|
10863
|
+
const cpDir = path33.join(root, CHECKPOINT_DIR, label);
|
|
10864
|
+
if (!fs30.existsSync(cpDir)) fs30.mkdirSync(cpDir, { recursive: true });
|
|
10865
|
+
fs30.copyFileSync(abs, path33.join(cpDir, path33.basename(abs)));
|
|
10866
|
+
fs30.writeFileSync(
|
|
10867
|
+
path33.join(cpDir, "manifest.json"),
|
|
10504
10868
|
JSON.stringify({
|
|
10505
10869
|
label,
|
|
10506
10870
|
timestamp: Date.now(),
|
|
@@ -10540,8 +10904,8 @@ __export(init_exports, {
|
|
|
10540
10904
|
generateInitMdContent: () => generateInitMdContent,
|
|
10541
10905
|
runInit: () => runInit
|
|
10542
10906
|
});
|
|
10543
|
-
import
|
|
10544
|
-
import
|
|
10907
|
+
import fs31 from "fs";
|
|
10908
|
+
import path34 from "path";
|
|
10545
10909
|
function configFilePath(type) {
|
|
10546
10910
|
switch (type) {
|
|
10547
10911
|
case "claude":
|
|
@@ -11175,21 +11539,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11175
11539
|
"",
|
|
11176
11540
|
"\u{1F4D6} Full rules: `.kuma/init.md`"
|
|
11177
11541
|
].join("\n");
|
|
11178
|
-
const skillPath =
|
|
11542
|
+
const skillPath = path34.resolve(root, ".agents", "skills", "kuma", "SKILL.md");
|
|
11179
11543
|
if (!results.some((r) => r.filePath === ".agents/skills/kuma/SKILL.md")) {
|
|
11180
11544
|
try {
|
|
11181
|
-
const dir =
|
|
11182
|
-
if (
|
|
11183
|
-
const existing =
|
|
11545
|
+
const dir = path34.dirname(skillPath);
|
|
11546
|
+
if (fs31.existsSync(skillPath)) {
|
|
11547
|
+
const existing = fs31.readFileSync(skillPath, "utf-8");
|
|
11184
11548
|
if (existing.includes("kuma-mcp")) {
|
|
11185
11549
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "skipped" });
|
|
11186
11550
|
} else {
|
|
11187
|
-
|
|
11551
|
+
fs31.writeFileSync(skillPath, existing.trimEnd() + "\n\n---\n\n" + opencodeContent, "utf-8");
|
|
11188
11552
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "appended" });
|
|
11189
11553
|
}
|
|
11190
11554
|
} else {
|
|
11191
|
-
if (!
|
|
11192
|
-
|
|
11555
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11556
|
+
fs31.writeFileSync(skillPath, opencodeContent, "utf-8");
|
|
11193
11557
|
results.push({ type: "opencode", filePath: ".agents/skills/kuma/SKILL.md", action: "created" });
|
|
11194
11558
|
}
|
|
11195
11559
|
} catch (err) {
|
|
@@ -11201,21 +11565,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11201
11565
|
});
|
|
11202
11566
|
}
|
|
11203
11567
|
}
|
|
11204
|
-
const opencodeSkillPath =
|
|
11568
|
+
const opencodeSkillPath = path34.resolve(root, ".opencode", "skills", "kuma", "SKILL.md");
|
|
11205
11569
|
if (!results.some((r) => r.filePath === ".opencode/skills/kuma/SKILL.md")) {
|
|
11206
11570
|
try {
|
|
11207
|
-
const dir =
|
|
11208
|
-
if (
|
|
11209
|
-
const existing =
|
|
11571
|
+
const dir = path34.dirname(opencodeSkillPath);
|
|
11572
|
+
if (fs31.existsSync(opencodeSkillPath)) {
|
|
11573
|
+
const existing = fs31.readFileSync(opencodeSkillPath, "utf-8");
|
|
11210
11574
|
if (existing.includes("kuma-mcp")) {
|
|
11211
11575
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "skipped" });
|
|
11212
11576
|
} else {
|
|
11213
|
-
|
|
11577
|
+
fs31.writeFileSync(opencodeSkillPath, existing.trimEnd() + "\n\n---\n\n" + opencodeContent, "utf-8");
|
|
11214
11578
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "appended" });
|
|
11215
11579
|
}
|
|
11216
11580
|
} else {
|
|
11217
|
-
if (!
|
|
11218
|
-
|
|
11581
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11582
|
+
fs31.writeFileSync(opencodeSkillPath, opencodeContent, "utf-8");
|
|
11219
11583
|
results.push({ type: "opencode", filePath: ".opencode/skills/kuma/SKILL.md", action: "created" });
|
|
11220
11584
|
}
|
|
11221
11585
|
} catch (err) {
|
|
@@ -11227,21 +11591,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11227
11591
|
});
|
|
11228
11592
|
}
|
|
11229
11593
|
}
|
|
11230
|
-
const pluginPath =
|
|
11594
|
+
const pluginPath = path34.resolve(root, ".opencode", "plugins", "kuma.js");
|
|
11231
11595
|
if (!results.some((r) => r.filePath === ".opencode/plugins/kuma.js")) {
|
|
11232
11596
|
try {
|
|
11233
|
-
const dir =
|
|
11234
|
-
if (
|
|
11235
|
-
const existing =
|
|
11597
|
+
const dir = path34.dirname(pluginPath);
|
|
11598
|
+
if (fs31.existsSync(pluginPath)) {
|
|
11599
|
+
const existing = fs31.readFileSync(pluginPath, "utf-8");
|
|
11236
11600
|
if (existing.includes("KumaPlugin") || existing.includes("kuma")) {
|
|
11237
11601
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "skipped" });
|
|
11238
11602
|
} else {
|
|
11239
|
-
|
|
11603
|
+
fs31.writeFileSync(pluginPath, existing.trimEnd() + "\n\n" + opencodePluginTemplate(), "utf-8");
|
|
11240
11604
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "appended" });
|
|
11241
11605
|
}
|
|
11242
11606
|
} else {
|
|
11243
|
-
if (!
|
|
11244
|
-
|
|
11607
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11608
|
+
fs31.writeFileSync(pluginPath, opencodePluginTemplate(), "utf-8");
|
|
11245
11609
|
results.push({ type: "opencode", filePath: ".opencode/plugins/kuma.js", action: "created" });
|
|
11246
11610
|
}
|
|
11247
11611
|
} catch (err) {
|
|
@@ -11255,21 +11619,21 @@ function handleOpencodeSecondary(root, results) {
|
|
|
11255
11619
|
}
|
|
11256
11620
|
}
|
|
11257
11621
|
function handleCodexSecondary(root, results) {
|
|
11258
|
-
const tomlPath =
|
|
11622
|
+
const tomlPath = path34.resolve(root, ".codex/config.toml");
|
|
11259
11623
|
if (results.some((r) => r.filePath === ".codex/config.toml")) return;
|
|
11260
11624
|
try {
|
|
11261
|
-
const dir =
|
|
11262
|
-
if (!
|
|
11263
|
-
if (
|
|
11264
|
-
const existingContent =
|
|
11625
|
+
const dir = path34.dirname(tomlPath);
|
|
11626
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11627
|
+
if (fs31.existsSync(tomlPath)) {
|
|
11628
|
+
const existingContent = fs31.readFileSync(tomlPath, "utf-8");
|
|
11265
11629
|
if (existingContent.includes("kuma")) {
|
|
11266
11630
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "skipped" });
|
|
11267
11631
|
return;
|
|
11268
11632
|
}
|
|
11269
|
-
|
|
11633
|
+
fs31.writeFileSync(tomlPath, existingContent.trimEnd() + "\n\n" + codexConfigTomlTemplate(), "utf-8");
|
|
11270
11634
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "appended" });
|
|
11271
11635
|
} else {
|
|
11272
|
-
|
|
11636
|
+
fs31.writeFileSync(tomlPath, codexConfigTomlTemplate(), "utf-8");
|
|
11273
11637
|
results.push({ type: "codex", filePath: ".codex/config.toml", action: "created" });
|
|
11274
11638
|
}
|
|
11275
11639
|
} catch (err) {
|
|
@@ -11282,11 +11646,11 @@ function handleCodexSecondary(root, results) {
|
|
|
11282
11646
|
}
|
|
11283
11647
|
}
|
|
11284
11648
|
function handleQwenSecondary(root, results) {
|
|
11285
|
-
const settingsPath =
|
|
11649
|
+
const settingsPath = path34.resolve(root, "settings.json");
|
|
11286
11650
|
if (results.some((r) => r.filePath === "settings.json")) return;
|
|
11287
11651
|
try {
|
|
11288
|
-
if (
|
|
11289
|
-
const existingContent =
|
|
11652
|
+
if (fs31.existsSync(settingsPath)) {
|
|
11653
|
+
const existingContent = fs31.readFileSync(settingsPath, "utf-8");
|
|
11290
11654
|
if (existingContent.includes("kuma")) {
|
|
11291
11655
|
if (!existingContent.includes("_Generated by Kuma MCP_")) {
|
|
11292
11656
|
try {
|
|
@@ -11294,7 +11658,7 @@ function handleQwenSecondary(root, results) {
|
|
|
11294
11658
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11295
11659
|
if (!parsed.mcpServers.kuma) {
|
|
11296
11660
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11297
|
-
|
|
11661
|
+
fs31.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11298
11662
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
11299
11663
|
return;
|
|
11300
11664
|
}
|
|
@@ -11309,13 +11673,13 @@ function handleQwenSecondary(root, results) {
|
|
|
11309
11673
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11310
11674
|
if (!parsed.mcpServers.kuma) {
|
|
11311
11675
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11312
|
-
|
|
11676
|
+
fs31.writeFileSync(settingsPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11313
11677
|
results.push({ type: "qwen", filePath: "settings.json", action: "appended" });
|
|
11314
11678
|
}
|
|
11315
11679
|
} catch {
|
|
11316
11680
|
}
|
|
11317
11681
|
} else {
|
|
11318
|
-
|
|
11682
|
+
fs31.writeFileSync(settingsPath, qwenSettingsTemplate(), "utf-8");
|
|
11319
11683
|
results.push({ type: "qwen", filePath: "settings.json", action: "created" });
|
|
11320
11684
|
}
|
|
11321
11685
|
} catch (err) {
|
|
@@ -11417,12 +11781,12 @@ function getCombinedAgentsMd(selectedTypes) {
|
|
|
11417
11781
|
return lines.join("\n");
|
|
11418
11782
|
}
|
|
11419
11783
|
function handleAntigravityMcpConfig(root, results) {
|
|
11420
|
-
const mcpPath =
|
|
11784
|
+
const mcpPath = path34.resolve(root, ".agents/mcp_config.json");
|
|
11421
11785
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
11422
11786
|
try {
|
|
11423
|
-
const mcpDir =
|
|
11424
|
-
if (
|
|
11425
|
-
const existingContent =
|
|
11787
|
+
const mcpDir = path34.dirname(mcpPath);
|
|
11788
|
+
if (fs31.existsSync(mcpPath)) {
|
|
11789
|
+
const existingContent = fs31.readFileSync(mcpPath, "utf-8");
|
|
11426
11790
|
if (existingContent.includes("kuma")) {
|
|
11427
11791
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "skipped" });
|
|
11428
11792
|
return;
|
|
@@ -11431,14 +11795,14 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
11431
11795
|
const parsed = JSON.parse(existingContent);
|
|
11432
11796
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11433
11797
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11434
|
-
|
|
11798
|
+
fs31.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11435
11799
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
11436
11800
|
} catch {
|
|
11437
11801
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "error", error: "Invalid JSON in existing mcp_config.json" });
|
|
11438
11802
|
}
|
|
11439
11803
|
} else {
|
|
11440
|
-
if (!
|
|
11441
|
-
|
|
11804
|
+
if (!fs31.existsSync(mcpDir)) fs31.mkdirSync(mcpDir, { recursive: true });
|
|
11805
|
+
fs31.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
11442
11806
|
results.push({ type: "antigravity", filePath: ".agents/mcp_config.json", action: "created" });
|
|
11443
11807
|
}
|
|
11444
11808
|
} catch (err) {
|
|
@@ -11451,21 +11815,21 @@ function handleAntigravityMcpConfig(root, results) {
|
|
|
11451
11815
|
}
|
|
11452
11816
|
}
|
|
11453
11817
|
function handleAntigravityRules(root, results) {
|
|
11454
|
-
const rulePath =
|
|
11818
|
+
const rulePath = path34.resolve(root, ".agents", "rules", "kuma.md");
|
|
11455
11819
|
if (results.some((r) => r.filePath === ".agents/rules/kuma.md")) return;
|
|
11456
11820
|
try {
|
|
11457
|
-
const dir =
|
|
11458
|
-
if (!
|
|
11459
|
-
if (
|
|
11460
|
-
const existing =
|
|
11821
|
+
const dir = path34.dirname(rulePath);
|
|
11822
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11823
|
+
if (fs31.existsSync(rulePath)) {
|
|
11824
|
+
const existing = fs31.readFileSync(rulePath, "utf-8");
|
|
11461
11825
|
if (existing.includes("Kuma MCP") || existing.includes("kuma-mcp")) {
|
|
11462
11826
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "skipped" });
|
|
11463
11827
|
return;
|
|
11464
11828
|
}
|
|
11465
|
-
|
|
11829
|
+
fs31.writeFileSync(rulePath, existing.trimEnd() + "\n\n---\n\n" + antigravityRuleTemplate(), "utf-8");
|
|
11466
11830
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "appended" });
|
|
11467
11831
|
} else {
|
|
11468
|
-
|
|
11832
|
+
fs31.writeFileSync(rulePath, antigravityRuleTemplate(), "utf-8");
|
|
11469
11833
|
results.push({ type: "antigravity", filePath: ".agents/rules/kuma.md", action: "created" });
|
|
11470
11834
|
}
|
|
11471
11835
|
} catch (err) {
|
|
@@ -11478,19 +11842,19 @@ function handleAntigravityRules(root, results) {
|
|
|
11478
11842
|
}
|
|
11479
11843
|
}
|
|
11480
11844
|
function handleGeminiMd(root, results) {
|
|
11481
|
-
const geminiPath =
|
|
11845
|
+
const geminiPath = path34.resolve(root, "GEMINI.md");
|
|
11482
11846
|
if (results.some((r) => r.filePath === "GEMINI.md")) return;
|
|
11483
11847
|
try {
|
|
11484
|
-
if (
|
|
11485
|
-
const existing =
|
|
11848
|
+
if (fs31.existsSync(geminiPath)) {
|
|
11849
|
+
const existing = fs31.readFileSync(geminiPath, "utf-8");
|
|
11486
11850
|
if (existing.includes("Kuma MCP") || existing.includes("kuma_context")) {
|
|
11487
11851
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "skipped" });
|
|
11488
11852
|
return;
|
|
11489
11853
|
}
|
|
11490
|
-
|
|
11854
|
+
fs31.writeFileSync(geminiPath, existing.trimEnd() + "\n\n---\n\n" + geminiTemplate(), "utf-8");
|
|
11491
11855
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "appended" });
|
|
11492
11856
|
} else {
|
|
11493
|
-
|
|
11857
|
+
fs31.writeFileSync(geminiPath, geminiTemplate(), "utf-8");
|
|
11494
11858
|
results.push({ type: "antigravity", filePath: "GEMINI.md", action: "created" });
|
|
11495
11859
|
}
|
|
11496
11860
|
} catch (err) {
|
|
@@ -11508,19 +11872,19 @@ function handleAntigravitySecondary(root, results) {
|
|
|
11508
11872
|
handleGeminiMd(root, results);
|
|
11509
11873
|
}
|
|
11510
11874
|
function handleWindsurfSecondary(root, results) {
|
|
11511
|
-
const rulesPath =
|
|
11875
|
+
const rulesPath = path34.resolve(root, ".windsurfrules");
|
|
11512
11876
|
if (results.some((r) => r.filePath === ".windsurfrules")) return;
|
|
11513
11877
|
try {
|
|
11514
|
-
if (
|
|
11515
|
-
const existing =
|
|
11878
|
+
if (fs31.existsSync(rulesPath)) {
|
|
11879
|
+
const existing = fs31.readFileSync(rulesPath, "utf-8");
|
|
11516
11880
|
if (existing.includes("Kuma MCP") || existing.includes("kuma_context")) {
|
|
11517
11881
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "skipped" });
|
|
11518
11882
|
return;
|
|
11519
11883
|
}
|
|
11520
|
-
|
|
11884
|
+
fs31.writeFileSync(rulesPath, existing.trimEnd() + "\n\n---\n\n" + windsurfrulesTemplate(), "utf-8");
|
|
11521
11885
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "appended" });
|
|
11522
11886
|
} else {
|
|
11523
|
-
|
|
11887
|
+
fs31.writeFileSync(rulesPath, windsurfrulesTemplate(), "utf-8");
|
|
11524
11888
|
results.push({ type: "windsurf", filePath: ".windsurfrules", action: "created" });
|
|
11525
11889
|
}
|
|
11526
11890
|
} catch (err) {
|
|
@@ -11533,21 +11897,21 @@ function handleWindsurfSecondary(root, results) {
|
|
|
11533
11897
|
}
|
|
11534
11898
|
}
|
|
11535
11899
|
function handleClineSecondary(root, results) {
|
|
11536
|
-
const rooPath =
|
|
11900
|
+
const rooPath = path34.resolve(root, ".roo", "rules", "kuma.md");
|
|
11537
11901
|
if (!results.some((r) => r.filePath === ".roo/rules/kuma.md")) {
|
|
11538
11902
|
try {
|
|
11539
|
-
const dir =
|
|
11540
|
-
if (
|
|
11541
|
-
const existing =
|
|
11903
|
+
const dir = path34.dirname(rooPath);
|
|
11904
|
+
if (fs31.existsSync(rooPath)) {
|
|
11905
|
+
const existing = fs31.readFileSync(rooPath, "utf-8");
|
|
11542
11906
|
if (existing.includes("kuma-mcp") || existing.includes("Kuma MCP")) {
|
|
11543
11907
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "skipped" });
|
|
11544
11908
|
} else {
|
|
11545
|
-
|
|
11909
|
+
fs31.writeFileSync(rooPath, existing.trimEnd() + "\n\n---\n\n" + rooRulesTemplate(), "utf-8");
|
|
11546
11910
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "appended" });
|
|
11547
11911
|
}
|
|
11548
11912
|
} else {
|
|
11549
|
-
if (!
|
|
11550
|
-
|
|
11913
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11914
|
+
fs31.writeFileSync(rooPath, rooRulesTemplate(), "utf-8");
|
|
11551
11915
|
results.push({ type: "cline", filePath: ".roo/rules/kuma.md", action: "created" });
|
|
11552
11916
|
}
|
|
11553
11917
|
} catch (err) {
|
|
@@ -11559,21 +11923,21 @@ function handleClineSecondary(root, results) {
|
|
|
11559
11923
|
});
|
|
11560
11924
|
}
|
|
11561
11925
|
}
|
|
11562
|
-
const traePath =
|
|
11926
|
+
const traePath = path34.resolve(root, ".trae", "rules", "kuma.md");
|
|
11563
11927
|
if (!results.some((r) => r.filePath === ".trae/rules/kuma.md")) {
|
|
11564
11928
|
try {
|
|
11565
|
-
const dir =
|
|
11566
|
-
if (
|
|
11567
|
-
const existing =
|
|
11929
|
+
const dir = path34.dirname(traePath);
|
|
11930
|
+
if (fs31.existsSync(traePath)) {
|
|
11931
|
+
const existing = fs31.readFileSync(traePath, "utf-8");
|
|
11568
11932
|
if (existing.includes("Kuma MCP")) {
|
|
11569
11933
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "skipped" });
|
|
11570
11934
|
} else {
|
|
11571
|
-
|
|
11935
|
+
fs31.writeFileSync(traePath, existing.trimEnd() + "\n\n---\n\n" + traeRulesTemplate(), "utf-8");
|
|
11572
11936
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "appended" });
|
|
11573
11937
|
}
|
|
11574
11938
|
} else {
|
|
11575
|
-
if (!
|
|
11576
|
-
|
|
11939
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11940
|
+
fs31.writeFileSync(traePath, traeRulesTemplate(), "utf-8");
|
|
11577
11941
|
results.push({ type: "cline", filePath: ".trae/rules/kuma.md", action: "created" });
|
|
11578
11942
|
}
|
|
11579
11943
|
} catch (err) {
|
|
@@ -11585,21 +11949,21 @@ function handleClineSecondary(root, results) {
|
|
|
11585
11949
|
});
|
|
11586
11950
|
}
|
|
11587
11951
|
}
|
|
11588
|
-
const qoderPath =
|
|
11952
|
+
const qoderPath = path34.resolve(root, ".qoder", "rules", "kuma.md");
|
|
11589
11953
|
if (!results.some((r) => r.filePath === ".qoder/rules/kuma.md")) {
|
|
11590
11954
|
try {
|
|
11591
|
-
const dir =
|
|
11592
|
-
if (
|
|
11593
|
-
const existing =
|
|
11955
|
+
const dir = path34.dirname(qoderPath);
|
|
11956
|
+
if (fs31.existsSync(qoderPath)) {
|
|
11957
|
+
const existing = fs31.readFileSync(qoderPath, "utf-8");
|
|
11594
11958
|
if (existing.includes("Kuma MCP")) {
|
|
11595
11959
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "skipped" });
|
|
11596
11960
|
} else {
|
|
11597
|
-
|
|
11961
|
+
fs31.writeFileSync(qoderPath, existing.trimEnd() + "\n\n---\n\n" + qoderRulesTemplate(), "utf-8");
|
|
11598
11962
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "appended" });
|
|
11599
11963
|
}
|
|
11600
11964
|
} else {
|
|
11601
|
-
if (!
|
|
11602
|
-
|
|
11965
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11966
|
+
fs31.writeFileSync(qoderPath, qoderRulesTemplate(), "utf-8");
|
|
11603
11967
|
results.push({ type: "cline", filePath: ".qoder/rules/kuma.md", action: "created" });
|
|
11604
11968
|
}
|
|
11605
11969
|
} catch (err) {
|
|
@@ -11613,18 +11977,18 @@ function handleClineSecondary(root, results) {
|
|
|
11613
11977
|
}
|
|
11614
11978
|
}
|
|
11615
11979
|
function handleAiderSecondary(root, results) {
|
|
11616
|
-
const ymlPath =
|
|
11980
|
+
const ymlPath = path34.resolve(root, ".aider.conf.yml");
|
|
11617
11981
|
if (results.some((r) => r.filePath === ".aider.conf.yml")) return;
|
|
11618
11982
|
try {
|
|
11619
11983
|
const conventionsRef = "read: CONVENTIONS.md";
|
|
11620
|
-
if (
|
|
11621
|
-
const existingContent =
|
|
11984
|
+
if (fs31.existsSync(ymlPath)) {
|
|
11985
|
+
const existingContent = fs31.readFileSync(ymlPath, "utf-8");
|
|
11622
11986
|
if (existingContent.includes("CONVENTIONS.md") || existingContent.includes("kuma")) {
|
|
11623
11987
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "skipped" });
|
|
11624
11988
|
return;
|
|
11625
11989
|
}
|
|
11626
11990
|
const newContent = existingContent.trimEnd() + "\n\n# Kuma MCP conventions\n" + conventionsRef + "\n";
|
|
11627
|
-
|
|
11991
|
+
fs31.writeFileSync(ymlPath, newContent, "utf-8");
|
|
11628
11992
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "appended" });
|
|
11629
11993
|
} else {
|
|
11630
11994
|
const content = [
|
|
@@ -11634,7 +11998,7 @@ function handleAiderSecondary(root, results) {
|
|
|
11634
11998
|
conventionsRef,
|
|
11635
11999
|
""
|
|
11636
12000
|
].join("\n");
|
|
11637
|
-
|
|
12001
|
+
fs31.writeFileSync(ymlPath, content, "utf-8");
|
|
11638
12002
|
results.push({ type: "aider", filePath: ".aider.conf.yml", action: "created" });
|
|
11639
12003
|
}
|
|
11640
12004
|
} catch (err) {
|
|
@@ -11647,10 +12011,10 @@ function handleAiderSecondary(root, results) {
|
|
|
11647
12011
|
}
|
|
11648
12012
|
}
|
|
11649
12013
|
function handleCopilotSecondary(root, results) {
|
|
11650
|
-
const skillPath =
|
|
12014
|
+
const skillPath = path34.resolve(root, ".github/skills/kuma/SKILL.md");
|
|
11651
12015
|
if (results.some((r) => r.filePath === ".github/skills/kuma/SKILL.md")) return;
|
|
11652
12016
|
try {
|
|
11653
|
-
const dir =
|
|
12017
|
+
const dir = path34.dirname(skillPath);
|
|
11654
12018
|
const content = [
|
|
11655
12019
|
"---",
|
|
11656
12020
|
"name: kuma-mcp",
|
|
@@ -11661,17 +12025,17 @@ function handleCopilotSecondary(root, results) {
|
|
|
11661
12025
|
"",
|
|
11662
12026
|
"\u{1F4D6} Read `.kuma/init.md` for detailed rules."
|
|
11663
12027
|
].join("\n");
|
|
11664
|
-
if (
|
|
11665
|
-
const existingContent =
|
|
12028
|
+
if (fs31.existsSync(skillPath)) {
|
|
12029
|
+
const existingContent = fs31.readFileSync(skillPath, "utf-8");
|
|
11666
12030
|
if (existingContent.includes("kuma")) {
|
|
11667
12031
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "skipped" });
|
|
11668
12032
|
return;
|
|
11669
12033
|
}
|
|
11670
|
-
|
|
12034
|
+
fs31.writeFileSync(skillPath, existingContent.trimEnd() + "\n\n" + content, "utf-8");
|
|
11671
12035
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "appended" });
|
|
11672
12036
|
} else {
|
|
11673
|
-
if (!
|
|
11674
|
-
|
|
12037
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
12038
|
+
fs31.writeFileSync(skillPath, content, "utf-8");
|
|
11675
12039
|
results.push({ type: "copilot", filePath: ".github/skills/kuma/SKILL.md", action: "created" });
|
|
11676
12040
|
}
|
|
11677
12041
|
} catch (err) {
|
|
@@ -11684,21 +12048,21 @@ function handleCopilotSecondary(root, results) {
|
|
|
11684
12048
|
}
|
|
11685
12049
|
}
|
|
11686
12050
|
function handleOpenclawSecondary(root, results) {
|
|
11687
|
-
const mcpPath =
|
|
12051
|
+
const mcpPath = path34.resolve(root, ".agents/mcp_config.json");
|
|
11688
12052
|
if (results.some((r) => r.filePath === ".agents/mcp_config.json")) return;
|
|
11689
12053
|
try {
|
|
11690
|
-
const dir =
|
|
11691
|
-
if (
|
|
11692
|
-
const existingContent =
|
|
12054
|
+
const dir = path34.dirname(mcpPath);
|
|
12055
|
+
if (fs31.existsSync(mcpPath)) {
|
|
12056
|
+
const existingContent = fs31.readFileSync(mcpPath, "utf-8");
|
|
11693
12057
|
if (existingContent.includes("kuma")) return;
|
|
11694
12058
|
const parsed = JSON.parse(existingContent);
|
|
11695
12059
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11696
12060
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11697
|
-
|
|
12061
|
+
fs31.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11698
12062
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "appended" });
|
|
11699
12063
|
} else {
|
|
11700
|
-
if (!
|
|
11701
|
-
|
|
12064
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
12065
|
+
fs31.writeFileSync(mcpPath, antigravityMcpConfigTemplate(), "utf-8");
|
|
11702
12066
|
results.push({ type: "openclaw", filePath: ".agents/mcp_config.json", action: "created" });
|
|
11703
12067
|
}
|
|
11704
12068
|
} catch (err) {
|
|
@@ -11711,20 +12075,20 @@ function handleOpenclawSecondary(root, results) {
|
|
|
11711
12075
|
}
|
|
11712
12076
|
}
|
|
11713
12077
|
function handleInitMdGeneration(root, results) {
|
|
11714
|
-
const initMdPath =
|
|
12078
|
+
const initMdPath = path34.resolve(root, ".kuma/init.md");
|
|
11715
12079
|
try {
|
|
11716
|
-
const kumaDir =
|
|
11717
|
-
if (!
|
|
11718
|
-
if (
|
|
11719
|
-
const existing =
|
|
12080
|
+
const kumaDir = path34.dirname(initMdPath);
|
|
12081
|
+
if (!fs31.existsSync(kumaDir)) fs31.mkdirSync(kumaDir, { recursive: true });
|
|
12082
|
+
if (fs31.existsSync(initMdPath)) {
|
|
12083
|
+
const existing = fs31.readFileSync(initMdPath, "utf-8");
|
|
11720
12084
|
if (existing.includes("_Generated by Kuma MCP_")) {
|
|
11721
12085
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "skipped" });
|
|
11722
12086
|
return;
|
|
11723
12087
|
}
|
|
11724
|
-
|
|
12088
|
+
fs31.writeFileSync(initMdPath, existing.trimEnd() + "\n\n" + generateInitMdContent(), "utf-8");
|
|
11725
12089
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "appended" });
|
|
11726
12090
|
} else {
|
|
11727
|
-
|
|
12091
|
+
fs31.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
|
|
11728
12092
|
results.push({ type: "claude", filePath: ".kuma/init.md", action: "created" });
|
|
11729
12093
|
}
|
|
11730
12094
|
} catch (err) {
|
|
@@ -11737,20 +12101,20 @@ function handleInitMdGeneration(root, results) {
|
|
|
11737
12101
|
}
|
|
11738
12102
|
}
|
|
11739
12103
|
function handleCodewhaleSecondary(root, results) {
|
|
11740
|
-
const mcpPath =
|
|
12104
|
+
const mcpPath = path34.resolve(root, ".codewhale/mcp.json");
|
|
11741
12105
|
if (results.some((r) => r.filePath === ".codewhale/mcp.json")) return;
|
|
11742
12106
|
try {
|
|
11743
|
-
const dir =
|
|
11744
|
-
if (
|
|
11745
|
-
const existingContent =
|
|
12107
|
+
const dir = path34.dirname(mcpPath);
|
|
12108
|
+
if (fs31.existsSync(mcpPath)) {
|
|
12109
|
+
const existingContent = fs31.readFileSync(mcpPath, "utf-8");
|
|
11746
12110
|
if (existingContent.includes("kuma")) return;
|
|
11747
12111
|
const parsed = JSON.parse(existingContent);
|
|
11748
12112
|
parsed.mcpServers = parsed.mcpServers || {};
|
|
11749
12113
|
parsed.mcpServers.kuma = { command: "npx", args: ["-y", "@plumpslabs/kuma"], env: {} };
|
|
11750
|
-
|
|
12114
|
+
fs31.writeFileSync(mcpPath, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
|
|
11751
12115
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "appended" });
|
|
11752
12116
|
} else {
|
|
11753
|
-
if (!
|
|
12117
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11754
12118
|
const config = {
|
|
11755
12119
|
mcpServers: {
|
|
11756
12120
|
kuma: {
|
|
@@ -11760,7 +12124,7 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
11760
12124
|
}
|
|
11761
12125
|
}
|
|
11762
12126
|
};
|
|
11763
|
-
|
|
12127
|
+
fs31.writeFileSync(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
11764
12128
|
results.push({ type: "codewhale", filePath: ".codewhale/mcp.json", action: "created" });
|
|
11765
12129
|
}
|
|
11766
12130
|
} catch (err) {
|
|
@@ -11773,7 +12137,7 @@ function handleCodewhaleSecondary(root, results) {
|
|
|
11773
12137
|
}
|
|
11774
12138
|
}
|
|
11775
12139
|
function handleClaudeSecondary(root, results) {
|
|
11776
|
-
const settingsPath =
|
|
12140
|
+
const settingsPath = path34.resolve(root, ".claude", "settings.json");
|
|
11777
12141
|
const hookBlock = {
|
|
11778
12142
|
hooks: {
|
|
11779
12143
|
PreToolUse: [
|
|
@@ -11804,8 +12168,8 @@ function handleClaudeSecondary(root, results) {
|
|
|
11804
12168
|
}
|
|
11805
12169
|
};
|
|
11806
12170
|
try {
|
|
11807
|
-
if (
|
|
11808
|
-
const existing = JSON.parse(
|
|
12171
|
+
if (fs31.existsSync(settingsPath)) {
|
|
12172
|
+
const existing = JSON.parse(fs31.readFileSync(settingsPath, "utf-8"));
|
|
11809
12173
|
const existingHooks = existing.hooks || {};
|
|
11810
12174
|
const mergedPre = [...existingHooks.PreToolUse || []];
|
|
11811
12175
|
for (const entry of hookBlock.hooks.PreToolUse) {
|
|
@@ -11829,12 +12193,12 @@ function handleClaudeSecondary(root, results) {
|
|
|
11829
12193
|
...existing,
|
|
11830
12194
|
hooks: { ...existingHooks, PreToolUse: mergedPre, SessionStart: mergedStart, PreCompact: mergedCompact }
|
|
11831
12195
|
};
|
|
11832
|
-
|
|
12196
|
+
fs31.writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
11833
12197
|
results.push({ type: "claude", filePath: ".claude/settings.json", action: "appended" });
|
|
11834
12198
|
} else {
|
|
11835
|
-
const dir =
|
|
11836
|
-
if (!
|
|
11837
|
-
|
|
12199
|
+
const dir = path34.dirname(settingsPath);
|
|
12200
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
12201
|
+
fs31.writeFileSync(settingsPath, JSON.stringify(hookBlock, null, 2) + "\n", "utf-8");
|
|
11838
12202
|
results.push({ type: "claude", filePath: ".claude/settings.json", action: "created" });
|
|
11839
12203
|
}
|
|
11840
12204
|
} catch (err) {
|
|
@@ -11845,10 +12209,10 @@ function handleClaudeSecondary(root, results) {
|
|
|
11845
12209
|
error: err instanceof Error ? err.message : String(err)
|
|
11846
12210
|
});
|
|
11847
12211
|
}
|
|
11848
|
-
const skillPath =
|
|
12212
|
+
const skillPath = path34.resolve(root, ".claude", "skills", "kuma", "SKILL.md");
|
|
11849
12213
|
if (!results.some((r) => r.filePath === ".claude/skills/kuma/SKILL.md")) {
|
|
11850
12214
|
try {
|
|
11851
|
-
const dir =
|
|
12215
|
+
const dir = path34.dirname(skillPath);
|
|
11852
12216
|
const content = [
|
|
11853
12217
|
"---",
|
|
11854
12218
|
"name: kuma-mcp",
|
|
@@ -11860,17 +12224,17 @@ function handleClaudeSecondary(root, results) {
|
|
|
11860
12224
|
"\u{1F4D6} Read `.kuma/init.md` for detailed rules.",
|
|
11861
12225
|
"\u{1F4C2} Readable state: `.kuma/KNOWN_GOTCHAS.md` (gotchas) \xB7 `.kuma/memories/decisions.md` (decisions)"
|
|
11862
12226
|
].join("\n");
|
|
11863
|
-
if (
|
|
11864
|
-
const existing =
|
|
12227
|
+
if (fs31.existsSync(skillPath)) {
|
|
12228
|
+
const existing = fs31.readFileSync(skillPath, "utf-8");
|
|
11865
12229
|
if (existing.includes("kuma-mcp")) {
|
|
11866
12230
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "skipped" });
|
|
11867
12231
|
} else {
|
|
11868
|
-
|
|
12232
|
+
fs31.writeFileSync(skillPath, existing.trimEnd() + "\n\n---\n\n" + content, "utf-8");
|
|
11869
12233
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "appended" });
|
|
11870
12234
|
}
|
|
11871
12235
|
} else {
|
|
11872
|
-
if (!
|
|
11873
|
-
|
|
12236
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
12237
|
+
fs31.writeFileSync(skillPath, content, "utf-8");
|
|
11874
12238
|
results.push({ type: "claude", filePath: ".claude/skills/kuma/SKILL.md", action: "created" });
|
|
11875
12239
|
}
|
|
11876
12240
|
} catch (err) {
|
|
@@ -11884,27 +12248,27 @@ function handleClaudeSecondary(root, results) {
|
|
|
11884
12248
|
}
|
|
11885
12249
|
}
|
|
11886
12250
|
function writeHooksFile(root, relPath, type, results, build) {
|
|
11887
|
-
const fullPath =
|
|
12251
|
+
const fullPath = path34.resolve(root, relPath);
|
|
11888
12252
|
try {
|
|
11889
12253
|
let existing = {};
|
|
11890
12254
|
let existed = false;
|
|
11891
|
-
if (
|
|
12255
|
+
if (fs31.existsSync(fullPath)) {
|
|
11892
12256
|
existed = true;
|
|
11893
12257
|
try {
|
|
11894
|
-
existing = JSON.parse(
|
|
12258
|
+
existing = JSON.parse(fs31.readFileSync(fullPath, "utf-8"));
|
|
11895
12259
|
} catch {
|
|
11896
12260
|
existing = {};
|
|
11897
12261
|
}
|
|
11898
12262
|
} else {
|
|
11899
|
-
const dir =
|
|
11900
|
-
if (!
|
|
12263
|
+
const dir = path34.dirname(fullPath);
|
|
12264
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
11901
12265
|
}
|
|
11902
12266
|
const { doc, changed } = build(existing);
|
|
11903
12267
|
if (!changed) {
|
|
11904
12268
|
results.push({ type, filePath: relPath, action: "skipped" });
|
|
11905
12269
|
return;
|
|
11906
12270
|
}
|
|
11907
|
-
|
|
12271
|
+
fs31.writeFileSync(fullPath, JSON.stringify(doc, null, 2) + "\n", "utf-8");
|
|
11908
12272
|
results.push({ type, filePath: relPath, action: existed ? "appended" : "created" });
|
|
11909
12273
|
} catch (err) {
|
|
11910
12274
|
results.push({ type, filePath: relPath, action: "error", error: err instanceof Error ? err.message : String(err) });
|
|
@@ -11985,18 +12349,18 @@ function handleCodexHooks(root, results) {
|
|
|
11985
12349
|
});
|
|
11986
12350
|
}
|
|
11987
12351
|
function handleCursorGotchaRules(root, results) {
|
|
11988
|
-
const rulesDir2 =
|
|
12352
|
+
const rulesDir2 = path34.resolve(root, ".cursor", "rules", "kuma-gotchas");
|
|
11989
12353
|
try {
|
|
11990
12354
|
let gotchas = [];
|
|
11991
12355
|
try {
|
|
11992
12356
|
gotchas = getActiveGotchas().filter((g) => g.severity === "high" || g.severity === "critical");
|
|
11993
12357
|
} catch {
|
|
11994
12358
|
}
|
|
11995
|
-
if (
|
|
11996
|
-
for (const f of
|
|
12359
|
+
if (fs31.existsSync(rulesDir2)) {
|
|
12360
|
+
for (const f of fs31.readdirSync(rulesDir2)) {
|
|
11997
12361
|
if (f.endsWith(".mdc")) {
|
|
11998
12362
|
try {
|
|
11999
|
-
|
|
12363
|
+
fs31.rmSync(path34.join(rulesDir2, f), { force: true });
|
|
12000
12364
|
} catch {
|
|
12001
12365
|
}
|
|
12002
12366
|
}
|
|
@@ -12006,7 +12370,7 @@ function handleCursorGotchaRules(root, results) {
|
|
|
12006
12370
|
results.push({ type: "cursor", filePath: ".cursor/rules/kuma-gotchas/", action: "skipped" });
|
|
12007
12371
|
return;
|
|
12008
12372
|
}
|
|
12009
|
-
|
|
12373
|
+
fs31.mkdirSync(rulesDir2, { recursive: true });
|
|
12010
12374
|
const seen = /* @__PURE__ */ new Set();
|
|
12011
12375
|
let created = 0;
|
|
12012
12376
|
for (const g of gotchas) {
|
|
@@ -12027,7 +12391,7 @@ function handleCursorGotchaRules(root, results) {
|
|
|
12027
12391
|
"`kuma_context({ action: 'history', target: '<file>' })`",
|
|
12028
12392
|
""
|
|
12029
12393
|
].join("\n");
|
|
12030
|
-
|
|
12394
|
+
fs31.writeFileSync(path34.join(rulesDir2, `${slug2}.mdc`), md, "utf-8");
|
|
12031
12395
|
created++;
|
|
12032
12396
|
}
|
|
12033
12397
|
results.push({
|
|
@@ -12054,28 +12418,28 @@ function runInit(options) {
|
|
|
12054
12418
|
let agentsMdHandled = false;
|
|
12055
12419
|
for (const type of selected) {
|
|
12056
12420
|
const relativePath = configFilePath(type);
|
|
12057
|
-
const fullPath =
|
|
12421
|
+
const fullPath = path34.resolve(root, relativePath);
|
|
12058
12422
|
const getTemplate = TEMPLATES[type];
|
|
12059
12423
|
try {
|
|
12060
12424
|
if (AGENTS_MD_TYPES.includes(type) && !agentsMdHandled) {
|
|
12061
12425
|
agentsMdHandled = true;
|
|
12062
12426
|
const combinedContent = getCombinedAgentsMd(new Set(agentsMdSelected));
|
|
12063
|
-
if (
|
|
12427
|
+
if (fs31.existsSync(fullPath)) {
|
|
12064
12428
|
if (options.skipExisting) {
|
|
12065
12429
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12066
12430
|
} else {
|
|
12067
|
-
const existingContent =
|
|
12431
|
+
const existingContent = fs31.readFileSync(fullPath, "utf-8");
|
|
12068
12432
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
12069
12433
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12070
12434
|
} else {
|
|
12071
|
-
|
|
12435
|
+
fs31.writeFileSync(fullPath, existingContent.trimEnd() + "\n\n" + combinedContent, "utf-8");
|
|
12072
12436
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
12073
12437
|
}
|
|
12074
12438
|
}
|
|
12075
12439
|
} else {
|
|
12076
|
-
const dir =
|
|
12077
|
-
if (!
|
|
12078
|
-
|
|
12440
|
+
const dir = path34.dirname(fullPath);
|
|
12441
|
+
if (!fs31.existsSync(dir)) fs31.mkdirSync(dir, { recursive: true });
|
|
12442
|
+
fs31.writeFileSync(fullPath, combinedContent, "utf-8");
|
|
12079
12443
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
12080
12444
|
}
|
|
12081
12445
|
if (selectedSet.has("opencode")) handleOpencodeSecondary(root, results);
|
|
@@ -12087,12 +12451,12 @@ function runInit(options) {
|
|
|
12087
12451
|
continue;
|
|
12088
12452
|
} else {
|
|
12089
12453
|
const template = getTemplate();
|
|
12090
|
-
if (
|
|
12454
|
+
if (fs31.existsSync(fullPath)) {
|
|
12091
12455
|
if (options.skipExisting) {
|
|
12092
12456
|
results.push({ type, filePath: relativePath, action: "skipped" });
|
|
12093
12457
|
continue;
|
|
12094
12458
|
}
|
|
12095
|
-
const existingContent =
|
|
12459
|
+
const existingContent = fs31.readFileSync(fullPath, "utf-8");
|
|
12096
12460
|
if (existingContent.includes("_Generated by Kuma MCP_")) {
|
|
12097
12461
|
if (type === "antigravity") {
|
|
12098
12462
|
handleAntigravitySecondary(root, results);
|
|
@@ -12109,14 +12473,14 @@ function runInit(options) {
|
|
|
12109
12473
|
continue;
|
|
12110
12474
|
}
|
|
12111
12475
|
const newContent = existingContent.trimEnd() + APPEND_SEPARATOR + template;
|
|
12112
|
-
|
|
12476
|
+
fs31.writeFileSync(fullPath, newContent, "utf-8");
|
|
12113
12477
|
results.push({ type, filePath: relativePath, action: "appended" });
|
|
12114
12478
|
} else {
|
|
12115
|
-
const dir =
|
|
12116
|
-
if (!
|
|
12117
|
-
|
|
12479
|
+
const dir = path34.dirname(fullPath);
|
|
12480
|
+
if (!fs31.existsSync(dir)) {
|
|
12481
|
+
fs31.mkdirSync(dir, { recursive: true });
|
|
12118
12482
|
}
|
|
12119
|
-
|
|
12483
|
+
fs31.writeFileSync(fullPath, template, "utf-8");
|
|
12120
12484
|
results.push({ type, filePath: relativePath, action: "created" });
|
|
12121
12485
|
}
|
|
12122
12486
|
if (type === "antigravity") {
|
|
@@ -12288,8 +12652,8 @@ var compactionContext_exports = {};
|
|
|
12288
12652
|
__export(compactionContext_exports, {
|
|
12289
12653
|
getCompactionContext: () => getCompactionContext
|
|
12290
12654
|
});
|
|
12291
|
-
import
|
|
12292
|
-
import
|
|
12655
|
+
import fs32 from "fs";
|
|
12656
|
+
import path35 from "path";
|
|
12293
12657
|
async function getCompactionContext() {
|
|
12294
12658
|
const parts = [];
|
|
12295
12659
|
try {
|
|
@@ -12302,8 +12666,8 @@ async function getCompactionContext() {
|
|
|
12302
12666
|
} catch {
|
|
12303
12667
|
}
|
|
12304
12668
|
try {
|
|
12305
|
-
const dbFile =
|
|
12306
|
-
if (
|
|
12669
|
+
const dbFile = path35.join(getProjectRoot(), ".kuma", "kuma.db");
|
|
12670
|
+
if (fs32.existsSync(dbFile)) {
|
|
12307
12671
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
12308
12672
|
const db = await getDb2();
|
|
12309
12673
|
const stmt = db.prepare(
|
|
@@ -12329,9 +12693,9 @@ async function getCompactionContext() {
|
|
|
12329
12693
|
} catch {
|
|
12330
12694
|
}
|
|
12331
12695
|
try {
|
|
12332
|
-
const cpDir =
|
|
12333
|
-
if (
|
|
12334
|
-
const cps =
|
|
12696
|
+
const cpDir = path35.join(getProjectRoot(), ".kuma", "checkpoints");
|
|
12697
|
+
if (fs32.existsSync(cpDir)) {
|
|
12698
|
+
const cps = fs32.readdirSync(cpDir).slice(0, 3);
|
|
12335
12699
|
if (cps.length > 0) parts.push(`Checkpoints: ${cps.join(", ")}`);
|
|
12336
12700
|
}
|
|
12337
12701
|
} catch {
|
|
@@ -12349,6 +12713,196 @@ var init_compactionContext = __esm({
|
|
|
12349
12713
|
}
|
|
12350
12714
|
});
|
|
12351
12715
|
|
|
12716
|
+
// src/engine/kumaDaemon.ts
|
|
12717
|
+
var kumaDaemon_exports = {};
|
|
12718
|
+
__export(kumaDaemon_exports, {
|
|
12719
|
+
DEFAULT_INTERVAL_MS: () => DEFAULT_INTERVAL_MS,
|
|
12720
|
+
daemonRunChild: () => daemonRunChild,
|
|
12721
|
+
daemonStart: () => daemonStart,
|
|
12722
|
+
daemonStatus: () => daemonStatus,
|
|
12723
|
+
daemonStop: () => daemonStop,
|
|
12724
|
+
daemonTick: () => daemonTick
|
|
12725
|
+
});
|
|
12726
|
+
import fs33 from "fs";
|
|
12727
|
+
import path36 from "path";
|
|
12728
|
+
import { spawn } from "child_process";
|
|
12729
|
+
function paths(root) {
|
|
12730
|
+
const r = root || getProjectRoot();
|
|
12731
|
+
return {
|
|
12732
|
+
pid: path36.join(r, PID_FILE),
|
|
12733
|
+
status: path36.join(r, STATUS_FILE),
|
|
12734
|
+
log: path36.join(r, LOG_FILE)
|
|
12735
|
+
};
|
|
12736
|
+
}
|
|
12737
|
+
function readPid(root) {
|
|
12738
|
+
try {
|
|
12739
|
+
const fp = paths(root).pid;
|
|
12740
|
+
if (!fs33.existsSync(fp)) return null;
|
|
12741
|
+
const pid = Number(fs33.readFileSync(fp, "utf-8").trim());
|
|
12742
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
12743
|
+
} catch {
|
|
12744
|
+
return null;
|
|
12745
|
+
}
|
|
12746
|
+
}
|
|
12747
|
+
function isAlive(pid) {
|
|
12748
|
+
try {
|
|
12749
|
+
process.kill(pid, 0);
|
|
12750
|
+
return true;
|
|
12751
|
+
} catch {
|
|
12752
|
+
return false;
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
async function daemonTick(root) {
|
|
12756
|
+
const r = root || getProjectRoot();
|
|
12757
|
+
const p = paths(r);
|
|
12758
|
+
const notes = [];
|
|
12759
|
+
try {
|
|
12760
|
+
const { ensureBackbone: ensureBackbone2, formatBackbone: formatBackbone2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
12761
|
+
const bb = await ensureBackbone2();
|
|
12762
|
+
if (bb) notes.push(formatBackbone2(bb));
|
|
12763
|
+
} catch {
|
|
12764
|
+
}
|
|
12765
|
+
try {
|
|
12766
|
+
const { syncModifiedFiles: syncModifiedFiles2 } = await Promise.resolve().then(() => (init_kumaCodeScanner(), kumaCodeScanner_exports));
|
|
12767
|
+
const scan = await syncModifiedFiles2(50);
|
|
12768
|
+
notes.push(`map: ${scan.filesScanned}f/${scan.nodeCount}n/${scan.edgeCount}e`);
|
|
12769
|
+
} catch (err) {
|
|
12770
|
+
notes.push(`map: skipped (${err})`);
|
|
12771
|
+
}
|
|
12772
|
+
try {
|
|
12773
|
+
const { refreshPackageBriefs: refreshPackageBriefs2 } = await Promise.resolve().then(() => (init_packageBriefs(), packageBriefs_exports));
|
|
12774
|
+
const { count } = await refreshPackageBriefs2();
|
|
12775
|
+
notes.push(`briefs: ${count}`);
|
|
12776
|
+
} catch {
|
|
12777
|
+
notes.push("briefs: skipped");
|
|
12778
|
+
}
|
|
12779
|
+
try {
|
|
12780
|
+
const { detectDrift: detectDrift2 } = await Promise.resolve().then(() => (init_kumaDriftDetector(), kumaDriftDetector_exports));
|
|
12781
|
+
const records = await detectDrift2();
|
|
12782
|
+
const stale = records.filter((x) => x.severity === "stale" || x.severity === "missing").length;
|
|
12783
|
+
notes.push(`drift: ${stale}/${records.length} stale`);
|
|
12784
|
+
} catch {
|
|
12785
|
+
notes.push("drift: skipped");
|
|
12786
|
+
}
|
|
12787
|
+
try {
|
|
12788
|
+
const { clearDirty: clearDirty2 } = await Promise.resolve().then(() => (init_cacheFreshness(), cacheFreshness_exports));
|
|
12789
|
+
clearDirty2();
|
|
12790
|
+
} catch {
|
|
12791
|
+
}
|
|
12792
|
+
const status = {
|
|
12793
|
+
lastRunAt: Date.now(),
|
|
12794
|
+
lastResult: notes.join(" \xB7 ")
|
|
12795
|
+
};
|
|
12796
|
+
try {
|
|
12797
|
+
let prev = {};
|
|
12798
|
+
if (fs33.existsSync(p.status)) prev = JSON.parse(fs33.readFileSync(p.status, "utf-8"));
|
|
12799
|
+
fs33.writeFileSync(p.status, JSON.stringify({ ...prev, ...status }, null, 2), "utf-8");
|
|
12800
|
+
} catch {
|
|
12801
|
+
}
|
|
12802
|
+
return notes.join(" \xB7 ");
|
|
12803
|
+
}
|
|
12804
|
+
async function runLoop(intervalMs) {
|
|
12805
|
+
const r = getProjectRoot();
|
|
12806
|
+
const p = paths(r);
|
|
12807
|
+
const log = (msg) => {
|
|
12808
|
+
try {
|
|
12809
|
+
fs33.appendFileSync(p.log, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
|
|
12810
|
+
`, "utf-8");
|
|
12811
|
+
} catch {
|
|
12812
|
+
}
|
|
12813
|
+
};
|
|
12814
|
+
log(`daemon started (interval ${intervalMs}ms)`);
|
|
12815
|
+
for (; ; ) {
|
|
12816
|
+
try {
|
|
12817
|
+
const res = await daemonTick(r);
|
|
12818
|
+
log(`tick: ${res}`);
|
|
12819
|
+
} catch (err) {
|
|
12820
|
+
log(`tick failed: ${err}`);
|
|
12821
|
+
}
|
|
12822
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
12823
|
+
}
|
|
12824
|
+
}
|
|
12825
|
+
async function daemonStart(intervalMs = DEFAULT_INTERVAL_MS, root) {
|
|
12826
|
+
const r = root || getProjectRoot();
|
|
12827
|
+
const p = paths(r);
|
|
12828
|
+
const existing = readPid(r);
|
|
12829
|
+
if (existing && isAlive(existing)) {
|
|
12830
|
+
return `\u{1F43B} Daemon already running (pid ${existing}). Use \`kuma daemon stop\` first.`;
|
|
12831
|
+
}
|
|
12832
|
+
const entry = process.argv[1];
|
|
12833
|
+
const child = spawn(process.execPath, [entry, "daemon", "run", String(intervalMs)], {
|
|
12834
|
+
cwd: r,
|
|
12835
|
+
detached: true,
|
|
12836
|
+
stdio: "ignore"
|
|
12837
|
+
});
|
|
12838
|
+
child.unref();
|
|
12839
|
+
try {
|
|
12840
|
+
fs33.writeFileSync(p.pid, String(child.pid), "utf-8");
|
|
12841
|
+
fs33.writeFileSync(
|
|
12842
|
+
p.status,
|
|
12843
|
+
JSON.stringify({ running: true, pid: child.pid, startedAt: Date.now(), lastRunAt: null, lastResult: null, intervalMs }, null, 2),
|
|
12844
|
+
"utf-8"
|
|
12845
|
+
);
|
|
12846
|
+
} catch {
|
|
12847
|
+
}
|
|
12848
|
+
return `\u{1F43B} Daemon started (pid ${child.pid}, every ${Math.round(intervalMs / 6e4)}m). Status: .kuma/daemon-status.json`;
|
|
12849
|
+
}
|
|
12850
|
+
async function daemonStop(root) {
|
|
12851
|
+
const r = root || getProjectRoot();
|
|
12852
|
+
const p = paths(r);
|
|
12853
|
+
const pid = readPid(r);
|
|
12854
|
+
if (!pid) return "\u{1F43B} Daemon not running (no pid file).";
|
|
12855
|
+
if (!isAlive(pid)) {
|
|
12856
|
+
try {
|
|
12857
|
+
fs33.rmSync(p.pid, { force: true });
|
|
12858
|
+
} catch {
|
|
12859
|
+
}
|
|
12860
|
+
return "\u{1F43B} Daemon pid file was stale \u2014 cleaned up (process already gone).";
|
|
12861
|
+
}
|
|
12862
|
+
try {
|
|
12863
|
+
process.kill(pid, "SIGTERM");
|
|
12864
|
+
try {
|
|
12865
|
+
fs33.rmSync(p.pid, { force: true });
|
|
12866
|
+
} catch {
|
|
12867
|
+
}
|
|
12868
|
+
return `\u{1F43B} Daemon stopped (was pid ${pid}).`;
|
|
12869
|
+
} catch (err) {
|
|
12870
|
+
return `\u{1F43B} Could not stop daemon pid ${pid}: ${err}`;
|
|
12871
|
+
}
|
|
12872
|
+
}
|
|
12873
|
+
async function daemonStatus(root) {
|
|
12874
|
+
const r = root || getProjectRoot();
|
|
12875
|
+
const p = paths(r);
|
|
12876
|
+
const pid = readPid(r);
|
|
12877
|
+
let status = { running: false, pid: null, startedAt: null, lastRunAt: null, lastResult: null, intervalMs: DEFAULT_INTERVAL_MS };
|
|
12878
|
+
try {
|
|
12879
|
+
if (fs33.existsSync(p.status)) status = { ...status, ...JSON.parse(fs33.readFileSync(p.status, "utf-8")) };
|
|
12880
|
+
} catch {
|
|
12881
|
+
}
|
|
12882
|
+
const alive = pid !== null && isAlive(pid);
|
|
12883
|
+
status.running = alive;
|
|
12884
|
+
status.pid = alive ? pid : null;
|
|
12885
|
+
if (!alive) {
|
|
12886
|
+
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\`.`;
|
|
12887
|
+
}
|
|
12888
|
+
return `\u{1F43B} Daemon: running (pid ${pid}).${status.lastRunAt ? ` Last tick ${new Date(status.lastRunAt).toISOString()}: ${status.lastResult || "n/a"}` : " Warming up\u2026"}`;
|
|
12889
|
+
}
|
|
12890
|
+
async function daemonRunChild(intervalMs) {
|
|
12891
|
+
const ms = Number.isFinite(intervalMs) && intervalMs >= 3e4 ? intervalMs : DEFAULT_INTERVAL_MS;
|
|
12892
|
+
await runLoop(ms);
|
|
12893
|
+
}
|
|
12894
|
+
var PID_FILE, STATUS_FILE, LOG_FILE, DEFAULT_INTERVAL_MS;
|
|
12895
|
+
var init_kumaDaemon = __esm({
|
|
12896
|
+
"src/engine/kumaDaemon.ts"() {
|
|
12897
|
+
"use strict";
|
|
12898
|
+
init_pathValidator();
|
|
12899
|
+
PID_FILE = ".kuma/daemon.pid";
|
|
12900
|
+
STATUS_FILE = ".kuma/daemon-status.json";
|
|
12901
|
+
LOG_FILE = ".kuma/daemon.log";
|
|
12902
|
+
DEFAULT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
12903
|
+
}
|
|
12904
|
+
});
|
|
12905
|
+
|
|
12352
12906
|
// src/utils/agentDetector.ts
|
|
12353
12907
|
var agentDetector_exports = {};
|
|
12354
12908
|
__export(agentDetector_exports, {
|
|
@@ -12357,19 +12911,19 @@ __export(agentDetector_exports, {
|
|
|
12357
12911
|
getSkillPath: () => getSkillPath,
|
|
12358
12912
|
skillExists: () => skillExists
|
|
12359
12913
|
});
|
|
12360
|
-
import
|
|
12361
|
-
import
|
|
12914
|
+
import fs34 from "fs";
|
|
12915
|
+
import path37 from "path";
|
|
12362
12916
|
function checkFile(root, filePath) {
|
|
12363
12917
|
try {
|
|
12364
|
-
return
|
|
12918
|
+
return fs34.existsSync(path37.join(root, filePath));
|
|
12365
12919
|
} catch {
|
|
12366
12920
|
return false;
|
|
12367
12921
|
}
|
|
12368
12922
|
}
|
|
12369
12923
|
function checkDir(root, dirPath) {
|
|
12370
12924
|
try {
|
|
12371
|
-
const fullPath =
|
|
12372
|
-
return
|
|
12925
|
+
const fullPath = path37.join(root, dirPath);
|
|
12926
|
+
return fs34.existsSync(fullPath) && fs34.statSync(fullPath).isDirectory();
|
|
12373
12927
|
} catch {
|
|
12374
12928
|
return false;
|
|
12375
12929
|
}
|
|
@@ -12437,7 +12991,7 @@ function getAgentLabel(type) {
|
|
|
12437
12991
|
function skillExists(type, projectRoot) {
|
|
12438
12992
|
const root = projectRoot ?? getProjectRoot();
|
|
12439
12993
|
const skillPath = getSkillPath(type);
|
|
12440
|
-
return
|
|
12994
|
+
return fs34.existsSync(path37.join(root, skillPath));
|
|
12441
12995
|
}
|
|
12442
12996
|
var AGENT_DETECTORS;
|
|
12443
12997
|
var init_agentDetector = __esm({
|
|
@@ -13033,8 +13587,8 @@ init_kumaGraph();
|
|
|
13033
13587
|
init_kumaMemory();
|
|
13034
13588
|
init_kumaDriftDetector();
|
|
13035
13589
|
init_pathValidator();
|
|
13036
|
-
import
|
|
13037
|
-
import
|
|
13590
|
+
import fs23 from "fs";
|
|
13591
|
+
import path26 from "path";
|
|
13038
13592
|
var CONTEXT_ALIASES = {
|
|
13039
13593
|
"research": "research",
|
|
13040
13594
|
"search": "research",
|
|
@@ -13153,6 +13707,15 @@ async function handleCluster(_params) {
|
|
|
13153
13707
|
async function handleInit(_params) {
|
|
13154
13708
|
sessionMemory.setGoal(_params.goal || "Exploring project");
|
|
13155
13709
|
sessionMemory.recordToolCall("kuma_context_init", {});
|
|
13710
|
+
let backboneNote = "";
|
|
13711
|
+
try {
|
|
13712
|
+
const { ensureBackbone: ensureBackbone2, formatBackbone: formatBackbone2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
13713
|
+
const bb = await ensureBackbone2();
|
|
13714
|
+
if (bb) backboneNote = `
|
|
13715
|
+
${formatBackbone2(bb)}
|
|
13716
|
+
`;
|
|
13717
|
+
} catch {
|
|
13718
|
+
}
|
|
13156
13719
|
const branch = sessionMemory.getCurrentBranch();
|
|
13157
13720
|
const branchTag = branch ? ` [git: \`${branch}\`]` : "";
|
|
13158
13721
|
const lines = [
|
|
@@ -13162,6 +13725,7 @@ async function handleInit(_params) {
|
|
|
13162
13725
|
`\u{1F4C1} Project: ${getProjectRoot().split("/").pop() || "unknown"}${branchTag}`,
|
|
13163
13726
|
""
|
|
13164
13727
|
];
|
|
13728
|
+
if (backboneNote) lines.push(backboneNote);
|
|
13165
13729
|
const branchTransition = sessionMemory.getBranchTransition();
|
|
13166
13730
|
if (branchTransition?.switched) {
|
|
13167
13731
|
lines.push(`\u{1F500} **Branch switched**: from \`${branchTransition.from}\` to \`${branchTransition.to}\`. Session context updated.`);
|
|
@@ -13361,6 +13925,14 @@ async function handleResearch(params, dirtySynced = 0) {
|
|
|
13361
13925
|
} else lines.push(" \u23F3 No content hash available \u2014 will research fresh");
|
|
13362
13926
|
lines.push("");
|
|
13363
13927
|
lines.push("**Step 3/5: Graph Query + Code Scan**");
|
|
13928
|
+
try {
|
|
13929
|
+
if (params.scope && (params.scope.includes("/") || params.scope.includes("."))) {
|
|
13930
|
+
const { ensureMapped: ensureMapped2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
13931
|
+
const { filled } = await ensureMapped2([params.scope]);
|
|
13932
|
+
if (filled > 0) lines.push(` \u{1F5FA}\uFE0F Precision tier filled on demand: ${filled} file(s) indexed (no user action needed)`);
|
|
13933
|
+
}
|
|
13934
|
+
} catch {
|
|
13935
|
+
}
|
|
13364
13936
|
try {
|
|
13365
13937
|
const graphResult = await searchGraph(scope, 15);
|
|
13366
13938
|
const graphLines = graphResult.split("\n");
|
|
@@ -13451,10 +14023,10 @@ async function handleHistory(params) {
|
|
|
13451
14023
|
lines.push(traceFormatted);
|
|
13452
14024
|
} else {
|
|
13453
14025
|
try {
|
|
13454
|
-
const { execSync:
|
|
14026
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
13455
14027
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
13456
14028
|
const root = getProjectRoot2();
|
|
13457
|
-
const gitHistory =
|
|
14029
|
+
const gitHistory = execSync10(
|
|
13458
14030
|
`git log -n 5 --pretty=format:" \u2022 %h (%ad) %s" --date=short -- "${target}"`,
|
|
13459
14031
|
{ cwd: root, encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
13460
14032
|
).trim();
|
|
@@ -13486,7 +14058,7 @@ ${gitHistory}`);
|
|
|
13486
14058
|
try {
|
|
13487
14059
|
const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
|
|
13488
14060
|
const db = await getDb2();
|
|
13489
|
-
const base =
|
|
14061
|
+
const base = path26.basename(target);
|
|
13490
14062
|
const stmt = db.prepare(`
|
|
13491
14063
|
SELECT description, last_verified_at FROM known_gotchas
|
|
13492
14064
|
WHERE status = 'resolved' AND (file_path LIKE ? OR file_path LIKE ?)
|
|
@@ -13499,9 +14071,9 @@ ${gitHistory}`);
|
|
|
13499
14071
|
if (resolved.length === 0) {
|
|
13500
14072
|
try {
|
|
13501
14073
|
const { getProjectRoot: getProjectRoot2 } = await Promise.resolve().then(() => (init_pathValidator(), pathValidator_exports));
|
|
13502
|
-
const mdPath =
|
|
13503
|
-
if (
|
|
13504
|
-
const mdContent =
|
|
14074
|
+
const mdPath = path26.join(getProjectRoot2(), ".kuma", "KNOWN_GOTCHAS.md");
|
|
14075
|
+
if (fs23.existsSync(mdPath)) {
|
|
14076
|
+
const mdContent = fs23.readFileSync(mdPath, "utf-8");
|
|
13505
14077
|
const sections = mdContent.split(/^###\s+/m);
|
|
13506
14078
|
for (const sec of sections) {
|
|
13507
14079
|
const secLower = sec.toLowerCase();
|
|
@@ -13573,8 +14145,8 @@ init_kumaDb();
|
|
|
13573
14145
|
init_kumaMemory();
|
|
13574
14146
|
init_pathValidator();
|
|
13575
14147
|
init_pathValidator();
|
|
13576
|
-
import
|
|
13577
|
-
import
|
|
14148
|
+
import fs25 from "fs";
|
|
14149
|
+
import path28 from "path";
|
|
13578
14150
|
var MEMORY_ALIASES = {
|
|
13579
14151
|
"decision": "decision",
|
|
13580
14152
|
"adr": "decision",
|
|
@@ -13685,9 +14257,9 @@ async function handleResearchSave(params) {
|
|
|
13685
14257
|
} catch {
|
|
13686
14258
|
}
|
|
13687
14259
|
try {
|
|
13688
|
-
const researchDir =
|
|
13689
|
-
if (!
|
|
13690
|
-
|
|
14260
|
+
const researchDir = path28.join(getProjectRoot(), ".kuma", "research");
|
|
14261
|
+
if (!fs25.existsSync(researchDir)) fs25.mkdirSync(researchDir, { recursive: true });
|
|
14262
|
+
fs25.writeFileSync(path28.join(researchDir, `${scope}.json`), JSON.stringify(JSON.parse(record), null, 2), "utf-8");
|
|
13691
14263
|
} catch {
|
|
13692
14264
|
}
|
|
13693
14265
|
sessionMemory.recordMemoryAction("research_save");
|
|
@@ -13883,9 +14455,9 @@ init_sessionMemory();
|
|
|
13883
14455
|
init_pathValidator();
|
|
13884
14456
|
init_sessionMemory();
|
|
13885
14457
|
init_kumaShared();
|
|
13886
|
-
import
|
|
13887
|
-
import
|
|
13888
|
-
import { execSync as
|
|
14458
|
+
import fs26 from "fs";
|
|
14459
|
+
import path29 from "path";
|
|
14460
|
+
import { execSync as execSync7 } from "child_process";
|
|
13889
14461
|
var SCRIPT_PATCH_PATTERNS = [
|
|
13890
14462
|
"writeFileSync",
|
|
13891
14463
|
"writeFile",
|
|
@@ -13911,20 +14483,20 @@ function findPatchScripts(projectRoot) {
|
|
|
13911
14483
|
const warnings = [];
|
|
13912
14484
|
const recentFiles = scanRecentFiles(projectRoot);
|
|
13913
14485
|
for (const file of recentFiles) {
|
|
13914
|
-
const ext =
|
|
14486
|
+
const ext = path29.extname(file).toLowerCase();
|
|
13915
14487
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
13916
|
-
const relativePath =
|
|
14488
|
+
const relativePath = path29.relative(projectRoot, file);
|
|
13917
14489
|
if (relativePath.startsWith("src") || relativePath.startsWith("test") || relativePath.startsWith("node_modules") || relativePath.startsWith(".") || relativePath.startsWith(".kuma/scratch")) {
|
|
13918
14490
|
continue;
|
|
13919
14491
|
}
|
|
13920
14492
|
try {
|
|
13921
|
-
const content =
|
|
14493
|
+
const content = fs26.readFileSync(file, "utf-8").toLowerCase();
|
|
13922
14494
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
13923
14495
|
if (matchedPattern) {
|
|
13924
14496
|
warnings.push({
|
|
13925
14497
|
severity: "high",
|
|
13926
14498
|
pattern: "script-patching",
|
|
13927
|
-
message: `Created script file that modifies other files: ${
|
|
14499
|
+
message: `Created script file that modifies other files: ${path29.basename(file)}`,
|
|
13928
14500
|
suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
|
|
13929
14501
|
evidence: `File: ${relativePath} contains '${matchedPattern}'`,
|
|
13930
14502
|
filePath: relativePath
|
|
@@ -13958,15 +14530,15 @@ function detectBashGrepUsage() {
|
|
|
13958
14530
|
function scanRecentFiles(projectRoot) {
|
|
13959
14531
|
const recent = [];
|
|
13960
14532
|
try {
|
|
13961
|
-
const entries =
|
|
14533
|
+
const entries = fs26.readdirSync(projectRoot, { withFileTypes: true });
|
|
13962
14534
|
const now = Date.now();
|
|
13963
14535
|
const recentThreshold = 30 * 60 * 1e3;
|
|
13964
14536
|
for (const entry of entries) {
|
|
13965
14537
|
if (!entry.isFile()) continue;
|
|
13966
14538
|
try {
|
|
13967
|
-
const stat =
|
|
14539
|
+
const stat = fs26.statSync(path29.join(projectRoot, entry.name));
|
|
13968
14540
|
if (now - stat.mtimeMs < recentThreshold || now - stat.ctimeMs < recentThreshold) {
|
|
13969
|
-
recent.push(
|
|
14541
|
+
recent.push(path29.join(projectRoot, entry.name));
|
|
13970
14542
|
}
|
|
13971
14543
|
} catch {
|
|
13972
14544
|
continue;
|
|
@@ -13979,7 +14551,7 @@ function scanRecentFiles(projectRoot) {
|
|
|
13979
14551
|
function detectGitPatchScripts(projectRoot) {
|
|
13980
14552
|
const warnings = [];
|
|
13981
14553
|
try {
|
|
13982
|
-
const statusStdout =
|
|
14554
|
+
const statusStdout = execSync7("git status --porcelain", {
|
|
13983
14555
|
cwd: projectRoot,
|
|
13984
14556
|
encoding: "utf-8",
|
|
13985
14557
|
timeout: 3e3
|
|
@@ -13990,14 +14562,14 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
13990
14562
|
const prefix = line.substring(0, 2);
|
|
13991
14563
|
if (prefix !== "??" && prefix !== "A ") continue;
|
|
13992
14564
|
const file = line.substring(3).trim();
|
|
13993
|
-
const ext =
|
|
14565
|
+
const ext = path29.extname(file).toLowerCase();
|
|
13994
14566
|
if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
|
|
13995
14567
|
const isRootLevel = !file.includes("/");
|
|
13996
14568
|
const isScriptsDir = file.startsWith("scripts/") || file.startsWith("patches/");
|
|
13997
14569
|
if (!isRootLevel && !isScriptsDir) continue;
|
|
13998
|
-
const fullPath =
|
|
13999
|
-
if (
|
|
14000
|
-
const content =
|
|
14570
|
+
const fullPath = path29.join(projectRoot, file);
|
|
14571
|
+
if (fs26.existsSync(fullPath)) {
|
|
14572
|
+
const content = fs26.readFileSync(fullPath, "utf-8").toLowerCase();
|
|
14001
14573
|
const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
|
|
14002
14574
|
if (matchedPattern) {
|
|
14003
14575
|
warnings.push({
|
|
@@ -14012,7 +14584,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
14012
14584
|
}
|
|
14013
14585
|
}
|
|
14014
14586
|
}
|
|
14015
|
-
const deletedStdout =
|
|
14587
|
+
const deletedStdout = execSync7("git diff --name-only --diff-filter=D HEAD", {
|
|
14016
14588
|
cwd: projectRoot,
|
|
14017
14589
|
encoding: "utf-8",
|
|
14018
14590
|
timeout: 3e3
|
|
@@ -14020,7 +14592,7 @@ function detectGitPatchScripts(projectRoot) {
|
|
|
14020
14592
|
if (deletedStdout) {
|
|
14021
14593
|
const deletedFiles = deletedStdout.split("\n").filter(Boolean);
|
|
14022
14594
|
for (const file of deletedFiles) {
|
|
14023
|
-
const ext =
|
|
14595
|
+
const ext = path29.extname(file).toLowerCase();
|
|
14024
14596
|
if (SCRIPT_EXTENSIONS.includes(ext)) {
|
|
14025
14597
|
warnings.push({
|
|
14026
14598
|
severity: "high",
|
|
@@ -14082,21 +14654,21 @@ function detectAllAntiPatterns() {
|
|
|
14082
14654
|
// src/engine/contextSnapshot.ts
|
|
14083
14655
|
init_sessionMemory();
|
|
14084
14656
|
init_pathValidator();
|
|
14085
|
-
import
|
|
14086
|
-
import
|
|
14087
|
-
import { execSync as
|
|
14657
|
+
import fs27 from "fs";
|
|
14658
|
+
import path30 from "path";
|
|
14659
|
+
import { execSync as execSync8 } from "child_process";
|
|
14088
14660
|
var SNAPSHOT_DIR = "context-snapshots";
|
|
14089
14661
|
function snapshotDir() {
|
|
14090
|
-
return
|
|
14662
|
+
return path30.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
|
|
14091
14663
|
}
|
|
14092
14664
|
function ensureSnapshotDir() {
|
|
14093
14665
|
const dir = snapshotDir();
|
|
14094
|
-
if (!
|
|
14095
|
-
|
|
14666
|
+
if (!fs27.existsSync(dir)) {
|
|
14667
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
14096
14668
|
}
|
|
14097
14669
|
}
|
|
14098
14670
|
function snapshotFilePath(timestamp) {
|
|
14099
|
-
return
|
|
14671
|
+
return path30.join(snapshotDir(), `${timestamp}.json`);
|
|
14100
14672
|
}
|
|
14101
14673
|
function saveSnapshot(goal) {
|
|
14102
14674
|
try {
|
|
@@ -14117,7 +14689,7 @@ function saveSnapshot(goal) {
|
|
|
14117
14689
|
hasConventions: !!summary.hasConventions
|
|
14118
14690
|
};
|
|
14119
14691
|
try {
|
|
14120
|
-
|
|
14692
|
+
fs27.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
|
|
14121
14693
|
} catch {
|
|
14122
14694
|
}
|
|
14123
14695
|
sessionMemory.recordToolCall("kuma_context", { action: "save", goal: snapshot.goal });
|
|
@@ -14156,7 +14728,7 @@ function formatSnapshot(snapshot) {
|
|
|
14156
14728
|
function getGitDiffStat2() {
|
|
14157
14729
|
try {
|
|
14158
14730
|
const root = getProjectRoot();
|
|
14159
|
-
const stdout =
|
|
14731
|
+
const stdout = execSync8("git diff --stat", {
|
|
14160
14732
|
cwd: root,
|
|
14161
14733
|
encoding: "utf-8",
|
|
14162
14734
|
timeout: 3e3
|
|
@@ -14817,6 +15389,7 @@ Usage:
|
|
|
14817
15389
|
npx @plumpslabs/kuma hook pre-compact PreCompact hook \u2014 survival set (goal + critical gotchas + dirty)
|
|
14818
15390
|
npx @plumpslabs/kuma hook post-commit Git post-commit \u2014 periodic incremental map sync (no rebuild)
|
|
14819
15391
|
npx @plumpslabs/kuma hook install-git Install the git post-commit hook (idempotent)
|
|
15392
|
+
npx @plumpslabs/kuma daemon start|stop|status Background sensors (map + briefs + drift) every 5m
|
|
14820
15393
|
|
|
14821
15394
|
Available config files:
|
|
14822
15395
|
--claude CLAUDE.md (Claude Code)
|
|
@@ -14909,14 +15482,14 @@ async function main() {
|
|
|
14909
15482
|
console.error("");
|
|
14910
15483
|
let killedCount = 0;
|
|
14911
15484
|
try {
|
|
14912
|
-
const
|
|
14913
|
-
const
|
|
14914
|
-
const lockDir =
|
|
14915
|
-
if (
|
|
14916
|
-
const pidFile =
|
|
14917
|
-
if (
|
|
15485
|
+
const path38 = await import("path");
|
|
15486
|
+
const fs35 = await import("fs");
|
|
15487
|
+
const lockDir = path38.resolve(process.cwd(), ".kuma/verifier.lock");
|
|
15488
|
+
if (fs35.existsSync(lockDir)) {
|
|
15489
|
+
const pidFile = path38.join(lockDir, "pid");
|
|
15490
|
+
if (fs35.existsSync(pidFile)) {
|
|
14918
15491
|
try {
|
|
14919
|
-
const pid = parseInt(
|
|
15492
|
+
const pid = parseInt(fs35.readFileSync(pidFile, "utf-8"), 10);
|
|
14920
15493
|
if (pid && pid !== process.pid) {
|
|
14921
15494
|
try {
|
|
14922
15495
|
process.kill(-pid, "SIGKILL");
|
|
@@ -14932,7 +15505,7 @@ async function main() {
|
|
|
14932
15505
|
} catch {
|
|
14933
15506
|
}
|
|
14934
15507
|
}
|
|
14935
|
-
|
|
15508
|
+
fs35.rmSync(lockDir, { recursive: true, force: true });
|
|
14936
15509
|
}
|
|
14937
15510
|
if (killedCount === 0) {
|
|
14938
15511
|
console.error("\u2705 No running verifications found to kill.");
|
|
@@ -14940,25 +15513,25 @@ async function main() {
|
|
|
14940
15513
|
} catch {
|
|
14941
15514
|
}
|
|
14942
15515
|
try {
|
|
14943
|
-
const { execSync:
|
|
15516
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
14944
15517
|
try {
|
|
14945
|
-
|
|
15518
|
+
execSync10("pkill -f 'jest' 2>/dev/null || true");
|
|
14946
15519
|
} catch {
|
|
14947
15520
|
}
|
|
14948
15521
|
try {
|
|
14949
|
-
|
|
15522
|
+
execSync10("pkill -f 'jest-worker' 2>/dev/null || true");
|
|
14950
15523
|
} catch {
|
|
14951
15524
|
}
|
|
14952
15525
|
try {
|
|
14953
|
-
|
|
15526
|
+
execSync10("pkill -f 'pnpm test' 2>/dev/null || true");
|
|
14954
15527
|
} catch {
|
|
14955
15528
|
}
|
|
14956
15529
|
try {
|
|
14957
|
-
|
|
15530
|
+
execSync10("pkill -f 'npm test' 2>/dev/null || true");
|
|
14958
15531
|
} catch {
|
|
14959
15532
|
}
|
|
14960
15533
|
try {
|
|
14961
|
-
|
|
15534
|
+
execSync10("pkill -f 'yarn test' 2>/dev/null || true");
|
|
14962
15535
|
} catch {
|
|
14963
15536
|
}
|
|
14964
15537
|
console.error("\u2705 Killed orphaned test processes");
|
|
@@ -14987,7 +15560,7 @@ async function main() {
|
|
|
14987
15560
|
if (args[0] === "studio") {
|
|
14988
15561
|
const { fileURLToPath } = await import("url");
|
|
14989
15562
|
const { dirname, join } = await import("path");
|
|
14990
|
-
const { spawn } = await import("child_process");
|
|
15563
|
+
const { spawn: spawn2 } = await import("child_process");
|
|
14991
15564
|
const { existsSync } = await import("fs");
|
|
14992
15565
|
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
14993
15566
|
const studioPath = join(distDir, "..", "packages", "ide", "studio", "dist", "index.js");
|
|
@@ -14998,7 +15571,7 @@ async function main() {
|
|
|
14998
15571
|
process.exit(1);
|
|
14999
15572
|
}
|
|
15000
15573
|
const studioArgs = args.slice(1);
|
|
15001
|
-
const child =
|
|
15574
|
+
const child = spawn2("node", [studioPath, ...studioArgs], {
|
|
15002
15575
|
stdio: "inherit",
|
|
15003
15576
|
cwd: process.cwd()
|
|
15004
15577
|
});
|
|
@@ -15131,14 +15704,14 @@ ${context}` : "";
|
|
|
15131
15704
|
process.stdout.write("\u{1F43B} [Kuma] git post-commit hook already installed.");
|
|
15132
15705
|
} else {
|
|
15133
15706
|
fsMod.appendFileSync(hookPath, `
|
|
15134
|
-
# Kuma periodic mapping
|
|
15707
|
+
# Kuma periodic mapping
|
|
15135
15708
|
${line}
|
|
15136
15709
|
`, "utf-8");
|
|
15137
15710
|
process.stdout.write("\u{1F43B} [Kuma] appended to existing git post-commit hook.");
|
|
15138
15711
|
}
|
|
15139
15712
|
} else {
|
|
15140
15713
|
fsMod.writeFileSync(hookPath, `#!/bin/sh
|
|
15141
|
-
# Kuma periodic mapping
|
|
15714
|
+
# Kuma periodic mapping
|
|
15142
15715
|
${line}
|
|
15143
15716
|
`, { mode: 493 });
|
|
15144
15717
|
process.stdout.write("\u{1F43B} [Kuma] git post-commit hook installed.");
|
|
@@ -15148,6 +15721,26 @@ ${line}
|
|
|
15148
15721
|
}
|
|
15149
15722
|
process.exit(0);
|
|
15150
15723
|
}
|
|
15724
|
+
if (args[0] === "daemon") {
|
|
15725
|
+
const sub = args[1] || "status";
|
|
15726
|
+
try {
|
|
15727
|
+
const { daemonStart: daemonStart2, daemonStop: daemonStop2, daemonStatus: daemonStatus2, daemonRunChild: daemonRunChild2, DEFAULT_INTERVAL_MS: DEFAULT_INTERVAL_MS2 } = await Promise.resolve().then(() => (init_kumaDaemon(), kumaDaemon_exports));
|
|
15728
|
+
if (sub === "start") {
|
|
15729
|
+
const ms = Number(args[2]) > 0 ? Number(args[2]) : DEFAULT_INTERVAL_MS2;
|
|
15730
|
+
process.stdout.write(await daemonStart2(ms));
|
|
15731
|
+
} else if (sub === "stop") {
|
|
15732
|
+
process.stdout.write(await daemonStop2());
|
|
15733
|
+
} else if (sub === "run") {
|
|
15734
|
+
await daemonRunChild2(Number(args[2]));
|
|
15735
|
+
return;
|
|
15736
|
+
} else {
|
|
15737
|
+
process.stdout.write(await daemonStatus2());
|
|
15738
|
+
}
|
|
15739
|
+
} catch (err) {
|
|
15740
|
+
process.stdout.write(`\u{1F43B} Daemon error: ${err}`);
|
|
15741
|
+
}
|
|
15742
|
+
process.exit(0);
|
|
15743
|
+
}
|
|
15151
15744
|
if (args[0] === "--hook") {
|
|
15152
15745
|
process.exit(0);
|
|
15153
15746
|
}
|
|
@@ -15158,8 +15751,14 @@ ${line}
|
|
|
15158
15751
|
const proj = root.split("/").pop() || "unknown";
|
|
15159
15752
|
const lines = [`\u{1F43B} [Kuma session brief] ${proj}`];
|
|
15160
15753
|
try {
|
|
15161
|
-
const {
|
|
15162
|
-
const
|
|
15754
|
+
const { ensureBackbone: ensureBackbone2, formatBackbone: formatBackbone2 } = await Promise.resolve().then(() => (init_mapBackbone(), mapBackbone_exports));
|
|
15755
|
+
const bb = await ensureBackbone2();
|
|
15756
|
+
if (bb) lines.push(formatBackbone2(bb));
|
|
15757
|
+
} catch {
|
|
15758
|
+
}
|
|
15759
|
+
try {
|
|
15760
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
15761
|
+
const branch = execSync10("git rev-parse --abbrev-ref HEAD", {
|
|
15163
15762
|
cwd: root,
|
|
15164
15763
|
encoding: "utf-8",
|
|
15165
15764
|
timeout: 2e3,
|
|
@@ -15277,23 +15876,23 @@ ${line}
|
|
|
15277
15876
|
});
|
|
15278
15877
|
const output = formatInitResults(results);
|
|
15279
15878
|
console.log(output);
|
|
15280
|
-
const
|
|
15281
|
-
const
|
|
15282
|
-
const matchaSkills =
|
|
15283
|
-
const matchaAgents =
|
|
15879
|
+
const fs35 = await import("fs");
|
|
15880
|
+
const path38 = await import("path");
|
|
15881
|
+
const matchaSkills = path38.resolve(process.cwd(), "skills/matcha/SKILL.md");
|
|
15882
|
+
const matchaAgents = path38.resolve(
|
|
15284
15883
|
process.cwd(),
|
|
15285
15884
|
".agents/skills/matcha/SKILL.md"
|
|
15286
15885
|
);
|
|
15287
|
-
const matchaRootSkills =
|
|
15886
|
+
const matchaRootSkills = path38.resolve(
|
|
15288
15887
|
process.cwd(),
|
|
15289
15888
|
"skills/matcha/SKILL.md"
|
|
15290
15889
|
);
|
|
15291
|
-
const matchaAgentsMd =
|
|
15292
|
-
const matchaWindsurfRules =
|
|
15890
|
+
const matchaAgentsMd = path38.resolve(process.cwd(), "AGENTS.md");
|
|
15891
|
+
const matchaWindsurfRules = path38.resolve(
|
|
15293
15892
|
process.cwd(),
|
|
15294
15893
|
".windsurf"
|
|
15295
15894
|
);
|
|
15296
|
-
if (
|
|
15895
|
+
if (fs35.existsSync(matchaSkills) || fs35.existsSync(matchaAgents) || fs35.existsSync(matchaRootSkills) || fs35.existsSync(matchaAgentsMd) || fs35.existsSync(matchaWindsurfRules)) {
|
|
15297
15896
|
console.error(
|
|
15298
15897
|
"\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!"
|
|
15299
15898
|
);
|
|
@@ -15307,13 +15906,13 @@ ${line}
|
|
|
15307
15906
|
(async () => {
|
|
15308
15907
|
try {
|
|
15309
15908
|
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
|
-
|
|
15909
|
+
const fs35 = await import("fs");
|
|
15910
|
+
const path38 = await import("path");
|
|
15911
|
+
const initMdPath = path38.resolve(process.cwd(), ".kuma/init.md");
|
|
15912
|
+
if (!fs35.existsSync(initMdPath)) {
|
|
15913
|
+
const kumaDir = path38.dirname(initMdPath);
|
|
15914
|
+
if (!fs35.existsSync(kumaDir)) fs35.mkdirSync(kumaDir, { recursive: true });
|
|
15915
|
+
fs35.writeFileSync(initMdPath, generateInitMdContent2(), "utf-8");
|
|
15317
15916
|
console.error(`[${SERVER_NAME}] Auto-generated .kuma/init.md`);
|
|
15318
15917
|
}
|
|
15319
15918
|
} catch (err) {
|
|
@@ -15324,8 +15923,8 @@ ${line}
|
|
|
15324
15923
|
try {
|
|
15325
15924
|
const { detectAgent: detectAgent2, getSkillPath: getSkillPath2, getAgentLabel: getAgentLabel2 } = await Promise.resolve().then(() => (init_agentDetector(), agentDetector_exports));
|
|
15326
15925
|
const { generateSkill: generateSkill2, getSecondaryFiles: getSecondaryFiles2 } = await Promise.resolve().then(() => (init_skillGenerator(), skillGenerator_exports));
|
|
15327
|
-
const
|
|
15328
|
-
const
|
|
15926
|
+
const fs35 = await import("fs");
|
|
15927
|
+
const path38 = await import("path");
|
|
15329
15928
|
const detection = detectAgent2();
|
|
15330
15929
|
if (!detection.primary) {
|
|
15331
15930
|
console.error(`[${SERVER_NAME}] No AI agent detected \u2014 skipping auto-skill creation`);
|
|
@@ -15333,22 +15932,22 @@ ${line}
|
|
|
15333
15932
|
}
|
|
15334
15933
|
const agentType = detection.primary;
|
|
15335
15934
|
const skillPath = getSkillPath2(agentType);
|
|
15336
|
-
const fullPath =
|
|
15337
|
-
if (
|
|
15935
|
+
const fullPath = path38.resolve(process.cwd(), skillPath);
|
|
15936
|
+
if (fs35.existsSync(fullPath)) {
|
|
15338
15937
|
console.error(`[${SERVER_NAME}] Skill exists for ${getAgentLabel2(agentType)} \u2014 skipping`);
|
|
15339
15938
|
return;
|
|
15340
15939
|
}
|
|
15341
|
-
const dir =
|
|
15342
|
-
if (!
|
|
15343
|
-
|
|
15940
|
+
const dir = path38.dirname(fullPath);
|
|
15941
|
+
if (!fs35.existsSync(dir)) fs35.mkdirSync(dir, { recursive: true });
|
|
15942
|
+
fs35.writeFileSync(fullPath, generateSkill2(agentType), "utf-8");
|
|
15344
15943
|
console.error(`[${SERVER_NAME}] Auto-created ${skillPath} for ${getAgentLabel2(agentType)}`);
|
|
15345
15944
|
const secondaryFiles = getSecondaryFiles2(agentType);
|
|
15346
15945
|
for (const sf of secondaryFiles) {
|
|
15347
|
-
const sfPath =
|
|
15348
|
-
const sfDir =
|
|
15349
|
-
if (!
|
|
15350
|
-
if (!
|
|
15351
|
-
|
|
15946
|
+
const sfPath = path38.resolve(process.cwd(), sf.path);
|
|
15947
|
+
const sfDir = path38.dirname(sfPath);
|
|
15948
|
+
if (!fs35.existsSync(sfPath)) {
|
|
15949
|
+
if (!fs35.existsSync(sfDir)) fs35.mkdirSync(sfDir, { recursive: true });
|
|
15950
|
+
fs35.writeFileSync(sfPath, sf.content, "utf-8");
|
|
15352
15951
|
console.error(`[${SERVER_NAME}] Auto-created ${sf.path}`);
|
|
15353
15952
|
}
|
|
15354
15953
|
}
|