@plumpslabs/kuma 2.2.6 → 2.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +975 -414
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -104,12 +104,89 @@ function isRipgrepAvailable() {
104
104
  function quotePath(p) {
105
105
  return `"${p.replace(/"/g, '\\"')}"`;
106
106
  }
107
+ async function searchInSingleFile(filePath, patterns, opts) {
108
+ const { maxResults, contextLines, filenamesOnly, countOnly, outputMode, compact } = opts;
109
+ const validation = validateFilePath(filePath);
110
+ if (!validation.valid) {
111
+ return compact ? `ERR:${filePath} - invalid path` : `Error: ${validation.error.message}`;
112
+ }
113
+ const resolvedPath = validation.resolvedPath;
114
+ if (!fs.existsSync(resolvedPath)) {
115
+ return compact ? `ERR:not found ${filePath}` : `Error: File not found: ${filePath}`;
116
+ }
117
+ const stat = fs.statSync(resolvedPath);
118
+ if (stat.size > 5e5) {
119
+ return compact ? `ERR:too large ${filePath}` : `Error: File too large (${(stat.size / 1024).toFixed(0)}KB). Use a more specific query.`;
120
+ }
121
+ if (isBinaryFile(resolvedPath)) {
122
+ return compact ? `ERR:binary ${filePath}` : `Error: Cannot search binary file: ${filePath}`;
123
+ }
124
+ const projectRoot = getProjectRoot();
125
+ const relativePath = resolvedPath.startsWith(projectRoot + "/") ? resolvedPath.slice(projectRoot.length + 1) : resolvedPath;
126
+ try {
127
+ const content = fs.readFileSync(resolvedPath, "utf-8");
128
+ const lines = content.split("\n");
129
+ const regex = createCombinedRegex(patterns);
130
+ const results = [];
131
+ const maxFileResults = Math.min(maxResults, 100);
132
+ let matchCount = 0;
133
+ for (let i = 0; i < lines.length; i++) {
134
+ if (results.length >= maxFileResults) break;
135
+ if (regex.test(lines[i])) {
136
+ matchCount++;
137
+ if (filenamesOnly) {
138
+ return relativePath;
139
+ }
140
+ if (countOnly) continue;
141
+ const ctxLines = [];
142
+ const startCtx = Math.max(0, i - contextLines);
143
+ const endCtx = Math.min(lines.length - 1, i + contextLines);
144
+ for (let ci = startCtx; ci <= endCtx; ci++) {
145
+ const prefix = ci === i ? ">" : " ";
146
+ ctxLines.push(`${prefix}${ci + 1}: ${lines[ci].substring(0, 200)}`);
147
+ }
148
+ results.push({
149
+ file: relativePath,
150
+ line: i + 1,
151
+ content: ctxLines.join("\n")
152
+ });
153
+ }
154
+ }
155
+ if (countOnly) {
156
+ const counts = `${filePath}:${matchCount}`;
157
+ if (outputMode === "json") return JSON.stringify({ [filePath]: matchCount });
158
+ if (compact) return counts;
159
+ return `\u{1F4CA} Count results for "${patterns[0]}":
160
+ ${counts}`;
161
+ }
162
+ if (matchCount === 0) {
163
+ if (outputMode === "json") return JSON.stringify({ query: patterns[0], matches: 0 });
164
+ if (compact) return `0:${patterns[0]}`;
165
+ return `\u{1F50D} No matches for "${patterns[0]}" in ${filePath}.`;
166
+ }
167
+ if (compact || outputMode === "raw") {
168
+ return results.map((r) => `${r.file}:${r.line}:${r.content.split("\n")[0].replace(/^[>\s]+\d+:\s*/, "")}`).join("\n");
169
+ }
170
+ if (outputMode === "json") {
171
+ return JSON.stringify(results);
172
+ }
173
+ const header = `\u{1F50D} Smart Grep: "${patterns[0]}" \u2014 ${filePath}
174
+ \u{1F4C1} ${results.length} matches
175
+ `;
176
+ const body = results.map((r, i) => `[${i + 1}] \u{1F4C4} ${r.file}:${r.line}
177
+ ${r.content}`).join("\n");
178
+ return limitLines(`${header}${body}`, 150);
179
+ } catch (err) {
180
+ return compact ? `ERR:reading ${filePath}` : `Error reading file "${filePath}": ${err instanceof Error ? err.message : String(err)}`;
181
+ }
182
+ }
107
183
  async function handleSmartGrep(params) {
108
184
  const {
109
185
  query,
110
186
  queries,
111
187
  targetFolder,
112
188
  maxResults = 30,
189
+ filePath,
113
190
  extensions,
114
191
  contextLines = 1,
115
192
  filenamesOnly = false,
@@ -121,7 +198,7 @@ async function handleSmartGrep(params) {
121
198
  if (patterns.length === 0 || patterns[0].length < 1) {
122
199
  return compact ? "ERR:query required" : "Error: 'query' or 'queries' parameter is required.";
123
200
  }
124
- const cacheKey = `${patterns.join("||")}:${targetFolder ?? "root"}:${maxResults}:${contextLines}:${filenamesOnly}:${countOnly}:${outputMode}:${extensions ? extensions.join(",") : ""}`;
201
+ const cacheKey = `${patterns.join("||")}:${filePath ?? ""}:${targetFolder ?? "root"}:${maxResults}:${contextLines}:${filenamesOnly}:${countOnly}:${outputMode}:${extensions ? extensions.join(",") : ""}`;
125
202
  const cached = grepCache.get(cacheKey);
126
203
  if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
127
204
  sessionMemory.recordToolCall("smart_grep", { query: patterns[0], cached: true });
@@ -129,7 +206,16 @@ async function handleSmartGrep(params) {
129
206
  }
130
207
  const projectRoot = getProjectRoot();
131
208
  let result;
132
- if (isRipgrepAvailable()) {
209
+ if (filePath) {
210
+ result = await searchInSingleFile(filePath, patterns, {
211
+ maxResults,
212
+ contextLines,
213
+ filenamesOnly,
214
+ countOnly,
215
+ outputMode,
216
+ compact
217
+ });
218
+ } else if (isRipgrepAvailable()) {
133
219
  result = await tryRipgrep(patterns, {
134
220
  projectRoot,
135
221
  targetFolder,
@@ -760,11 +846,48 @@ var CircuitBreakerStore = class {
760
846
  var circuitBreaker = new CircuitBreakerStore();
761
847
 
762
848
  // src/tools/preciseDiffEditor.ts
849
+ function fastApplyEdit(content, edit) {
850
+ const { searchBlock, replaceBlock, allowMultiple = false } = edit;
851
+ const count = countOccurrences(content, searchBlock);
852
+ if (count === 0) {
853
+ return {
854
+ success: false,
855
+ matched: 0,
856
+ replaced: 0,
857
+ error: `DIFF_MISMATCH: searchBlock not found (fast mode - exact match only)`,
858
+ details: content
859
+ };
860
+ }
861
+ const newContent = allowMultiple ? replaceAll(content, searchBlock, replaceBlock) : content.replace(searchBlock, replaceBlock);
862
+ return {
863
+ success: true,
864
+ matched: count,
865
+ replaced: allowMultiple ? count : 1,
866
+ details: newContent
867
+ };
868
+ }
869
+ function formatFastEditResult(results, filePath) {
870
+ const successCount = results.filter((r) => r.success).length;
871
+ const failCount = results.filter((r) => !r.success).length;
872
+ if (failCount === 0) {
873
+ return `\u26A1 Fast edit: ${filePath} \u2014 ${successCount} edits applied.`;
874
+ }
875
+ const lines = [`\u26A1 Fast edit: ${filePath}`];
876
+ for (let i = 0; i < results.length; i++) {
877
+ const r = results[i];
878
+ if (r.success) {
879
+ lines.push(` [${i + 1}] \u2705 ${r.matched}x matched, ${r.replaced}x replaced`);
880
+ } else {
881
+ lines.push(` [${i + 1}] \u274C ${r.error}`);
882
+ }
883
+ }
884
+ return lines.join("\n");
885
+ }
763
886
  function generateEditId() {
764
887
  return `edit-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
765
888
  }
766
889
  async function handlePreciseDiffEditor(params) {
767
- const { filePath, edits, dryRun = false, action, scope } = params;
890
+ const { filePath, edits, safe = true, dryRun = false, action, scope } = params;
768
891
  if (action === "rollback") {
769
892
  return handleRollbackEdit({ filePath, version: params.version, scope: scope || "file", editId: params.editId });
770
893
  }
@@ -776,11 +899,13 @@ async function handlePreciseDiffEditor(params) {
776
899
  return `Error: ${validation.error.message}`;
777
900
  }
778
901
  const resolvedPath = validation.resolvedPath;
779
- const cbResult = circuitBreaker.check("precise_diff_editor", { filePath });
780
- if (!cbResult.allowed) {
781
- return `\u26A0\uFE0F ${cbResult.reason}
902
+ if (safe) {
903
+ const cbResult = circuitBreaker.check("precise_diff_editor", { filePath });
904
+ if (!cbResult.allowed) {
905
+ return `\u26A0\uFE0F ${cbResult.reason}
782
906
 
783
907
  Try reading the file first with smart_file_picker to verify current content.`;
908
+ }
784
909
  }
785
910
  if (!fs3.existsSync(resolvedPath)) {
786
911
  return `Error: File not found: "${filePath}".
@@ -792,6 +917,22 @@ Use batch_file_writer to create a new file.`;
792
917
  const results = [];
793
918
  for (let i = 0; i < edits.length; i++) {
794
919
  const edit = edits[i];
920
+ if (!safe) {
921
+ const result2 = fastApplyEdit(currentContent, edit);
922
+ if (result2.success) {
923
+ fs3.writeFileSync(resolvedPath, result2.details, "utf-8");
924
+ currentContent = result2.details;
925
+ sessionMemory.recordToolCall("precise_diff_editor", {
926
+ filePath,
927
+ editIndex: i,
928
+ fast: true,
929
+ success: true
930
+ });
931
+ sessionMemory.addModifiedFile(filePath);
932
+ }
933
+ results.push(result2);
934
+ continue;
935
+ }
795
936
  const result = applyEdit(currentContent, edit, resolvedPath, i, dryRun);
796
937
  if (result.success) {
797
938
  if (!dryRun) {
@@ -817,6 +958,9 @@ Use batch_file_writer to create a new file.`;
817
958
  if (dryRun) {
818
959
  return formatDryRunResult(results, filePath, originalContent);
819
960
  }
961
+ if (!safe) {
962
+ return formatFastEditResult(results, filePath);
963
+ }
820
964
  return formatDiffResult(results, filePath);
821
965
  } catch (err) {
822
966
  return `Error editing file "${filePath}": ${err instanceof Error ? err.message : String(err)}`;
@@ -1196,10 +1340,10 @@ File "${filePath}" restored from backup: "${relBackupPath}".`;
1196
1340
  }
1197
1341
  async function handleCommitRollback(filePath, version) {
1198
1342
  try {
1199
- const { execSync: execSync11 } = await import("child_process");
1343
+ const { execSync: execSync13 } = await import("child_process");
1200
1344
  const root = getProjectRoot();
1201
1345
  if (version === "list") {
1202
- const log = execSync11("git log --oneline -20", { cwd: root, encoding: "utf-8" });
1346
+ const log = execSync13("git log --oneline -20", { cwd: root, encoding: "utf-8" });
1203
1347
  const lines = log.trim().split("\n").map((l) => ` ${l}`).join("\n");
1204
1348
  return [
1205
1349
  `\u{1F4CB} **Recent Commits:**`,
@@ -1209,7 +1353,7 @@ async function handleCommitRollback(filePath, version) {
1209
1353
  `\u{1F4A1} Use { action: "rollback", scope: "commit", version: <N> } to restore a specific commit.`
1210
1354
  ].join("\n");
1211
1355
  }
1212
- const status = execSync11("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
1356
+ const status = execSync13("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
1213
1357
  if (status) {
1214
1358
  return [
1215
1359
  `\u26A0\uFE0F **Uncommitted changes detected.**`,
@@ -1230,10 +1374,10 @@ async function handleCommitRollback(filePath, version) {
1230
1374
  target = "HEAD~1";
1231
1375
  }
1232
1376
  if (filePath) {
1233
- execSync11(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
1377
+ execSync13(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
1234
1378
  return `\u2705 **Commit Rollback** \u2014 File "${filePath}" restored from ${target}.`;
1235
1379
  } else {
1236
- execSync11(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
1380
+ execSync13(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
1237
1381
  return `\u2705 **Commit Rollback** \u2014 Project restored to ${target}.`;
1238
1382
  }
1239
1383
  } catch (err) {
@@ -1509,13 +1653,419 @@ function formatBatchResult(results, totalRequested) {
1509
1653
  return lines.join("\n");
1510
1654
  }
1511
1655
 
1512
- // src/tools/safeTerminalExec.ts
1513
- import fs6 from "fs";
1656
+ // src/tools/kumaFind.ts
1657
+ import fg2 from "fast-glob";
1658
+ import path5 from "path";
1659
+ import { execSync as execSync2 } from "child_process";
1660
+ var IGNORE_PATTERNS2 = [
1661
+ "**/node_modules/**",
1662
+ "**/.next/**",
1663
+ "**/dist/**",
1664
+ "**/build/**",
1665
+ "**/.git/**",
1666
+ "**/.cache/**",
1667
+ "**/.kuma/backups/**",
1668
+ "**/coverage/**",
1669
+ "**/.nyc_output/**"
1670
+ ];
1671
+ var CACHE_TTL_MS2 = 3e4;
1672
+ var findCache = /* @__PURE__ */ new Map();
1673
+ var rgAvailable2 = null;
1674
+ function isRipgrepAvailable2() {
1675
+ if (process.env.KUMA_DISABLE_RG === "1" || process.env.KUMA_DISABLE_RG === "true") {
1676
+ return false;
1677
+ }
1678
+ if (rgAvailable2 !== null) return rgAvailable2;
1679
+ try {
1680
+ execSync2("rg --version", { stdio: "ignore", timeout: 2e3 });
1681
+ rgAvailable2 = true;
1682
+ } catch {
1683
+ rgAvailable2 = false;
1684
+ }
1685
+ return rgAvailable2;
1686
+ }
1687
+ function quotePath2(p) {
1688
+ return `"${p.replace(/"/g, '\\"')}"`;
1689
+ }
1690
+ async function handleKumaFind(params) {
1691
+ const {
1692
+ name = "*",
1693
+ path: pathPattern,
1694
+ extensions,
1695
+ targetFolder,
1696
+ maxResults = 30,
1697
+ type = "file",
1698
+ outputMode = "rich",
1699
+ compact = false
1700
+ } = params;
1701
+ const recursiveName = !pathPattern && !targetFolder && !name.startsWith("**/") ? `**/${name}` : name;
1702
+ const cacheKey = `${recursiveName}:${pathPattern ?? ""}:${targetFolder ?? ""}:${type}:${maxResults}:${extensions ? extensions.join(",") : ""}`;
1703
+ const cached = findCache.get(cacheKey);
1704
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS2) {
1705
+ return cached.results;
1706
+ }
1707
+ const projectRoot = getProjectRoot();
1708
+ let entries;
1709
+ if (isRipgrepAvailable2() && type !== "dir") {
1710
+ entries = await tryRipgrepFind({
1711
+ name: recursiveName,
1712
+ pathPattern,
1713
+ targetFolder,
1714
+ extensions,
1715
+ maxResults,
1716
+ projectRoot
1717
+ });
1718
+ } else {
1719
+ entries = await fastGlobFind({
1720
+ name: recursiveName,
1721
+ pathPattern,
1722
+ targetFolder,
1723
+ extensions,
1724
+ maxResults,
1725
+ type,
1726
+ projectRoot
1727
+ });
1728
+ }
1729
+ if (entries.length === 0) {
1730
+ const noResult = compact ? `0:${name}` : `\u{1F50D} No files found matching "${name}"${targetFolder ? ` in ${targetFolder}` : ""}.`;
1731
+ findCache.set(cacheKey, { results: noResult, timestamp: Date.now() });
1732
+ return noResult;
1733
+ }
1734
+ sessionMemory.recordToolCall("kuma_find", {
1735
+ name,
1736
+ matchCount: entries.length,
1737
+ engine: rgAvailable2 ? "ripgrep" : "fast-glob"
1738
+ });
1739
+ let result;
1740
+ if (outputMode === "json") {
1741
+ result = JSON.stringify(entries);
1742
+ } else if (compact || outputMode === "raw") {
1743
+ result = entries.join("\n");
1744
+ } else {
1745
+ const lines = [
1746
+ `\u{1F50D} Find: "${name}"`,
1747
+ `\u{1F4C1} ${entries.length} files found${targetFolder ? ` in ${targetFolder}` : ""}`,
1748
+ "",
1749
+ ...entries.map((e, i) => `[${i + 1}] \u{1F4C4} ${e}`)
1750
+ ];
1751
+ result = lines.join("\n");
1752
+ }
1753
+ findCache.set(cacheKey, { results: result, timestamp: Date.now() });
1754
+ return result;
1755
+ }
1756
+ async function tryRipgrepFind(opts) {
1757
+ const { name, pathPattern, targetFolder, extensions, maxResults, projectRoot } = opts;
1758
+ const args = ["rg", "--files", "--no-ignore-vcs", "--color", "never"];
1759
+ for (const ignore of IGNORE_PATTERNS2) {
1760
+ const glob = ignore.replace(/^\*\*\//, "");
1761
+ args.push("-g", `!${glob}`);
1762
+ }
1763
+ args.push("-g", name);
1764
+ if (pathPattern) {
1765
+ args.push("-g", pathPattern);
1766
+ }
1767
+ if (extensions && extensions.length > 0) {
1768
+ for (const ext of extensions) {
1769
+ const clean = ext.startsWith(".") ? ext : `.${ext}`;
1770
+ args.push("-g", `*${clean}`);
1771
+ }
1772
+ }
1773
+ const searchDir = targetFolder ? path5.join(projectRoot, targetFolder) : projectRoot;
1774
+ args.push(quotePath2(searchDir));
1775
+ try {
1776
+ const output = execSync2(args.join(" "), {
1777
+ cwd: projectRoot,
1778
+ maxBuffer: 5 * 1024 * 1024,
1779
+ timeout: 1e4,
1780
+ encoding: "utf-8",
1781
+ stdio: ["ignore", "pipe", "pipe"]
1782
+ });
1783
+ if (!output || output.trim().length === 0) return [];
1784
+ let files = output.trim().split("\n").map((f) => f.trim()).filter(Boolean);
1785
+ if (targetFolder) {
1786
+ const prefix = targetFolder.replace(/\/+$/, "") + "/";
1787
+ files = files.map((f) => prefix + f);
1788
+ }
1789
+ return files.slice(0, maxResults);
1790
+ } catch (err) {
1791
+ return [];
1792
+ }
1793
+ }
1794
+ async function fastGlobFind(opts) {
1795
+ const { name, pathPattern, targetFolder, extensions, maxResults, type, projectRoot } = opts;
1796
+ const globParts = [];
1797
+ if (pathPattern) {
1798
+ globParts.push(pathPattern.replace(/^\/+/, "").replace(/\/+$/, ""));
1799
+ } else if (targetFolder) {
1800
+ globParts.push(targetFolder.replace(/^\/+/, "").replace(/\/+$/, ""));
1801
+ }
1802
+ globParts.push(name);
1803
+ const searchPattern = globParts.join("/");
1804
+ try {
1805
+ let entries = await fg2(searchPattern, {
1806
+ cwd: projectRoot,
1807
+ ignore: IGNORE_PATTERNS2,
1808
+ onlyFiles: type === "file",
1809
+ onlyDirectories: type === "dir",
1810
+ absolute: false,
1811
+ deep: 20,
1812
+ dot: false
1813
+ });
1814
+ if (extensions && extensions.length > 0 && type !== "dir") {
1815
+ const normalizedExts = extensions.map(
1816
+ (e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
1817
+ );
1818
+ entries = entries.filter((entry) => {
1819
+ const ext = path5.extname(entry).toLowerCase();
1820
+ return normalizedExts.includes(ext);
1821
+ });
1822
+ }
1823
+ return entries.slice(0, maxResults);
1824
+ } catch {
1825
+ return [];
1826
+ }
1827
+ }
1828
+
1829
+ // src/tools/kumaStats.ts
1830
+ import fs5 from "fs";
1514
1831
  import path6 from "path";
1832
+ import { execSync as execSync3 } from "child_process";
1833
+ import fg3 from "fast-glob";
1834
+ var LANG_MAP = {
1835
+ ts: "TypeScript",
1836
+ tsx: "TypeScript React",
1837
+ js: "JavaScript",
1838
+ jsx: "JavaScript React",
1839
+ py: "Python",
1840
+ go: "Go",
1841
+ rs: "Rust",
1842
+ java: "Java",
1843
+ kt: "Kotlin",
1844
+ rb: "Ruby",
1845
+ php: "PHP",
1846
+ cs: "C#",
1847
+ swift: "Swift",
1848
+ c: "C",
1849
+ cpp: "C++",
1850
+ h: "C/C++ Header",
1851
+ css: "CSS",
1852
+ scss: "SCSS",
1853
+ less: "Less",
1854
+ html: "HTML",
1855
+ json: "JSON",
1856
+ yaml: "YAML",
1857
+ yml: "YAML",
1858
+ md: "Markdown",
1859
+ sql: "SQL",
1860
+ graphql: "GraphQL",
1861
+ prisma: "Prisma",
1862
+ toml: "TOML",
1863
+ xml: "XML",
1864
+ svg: "SVG",
1865
+ sh: "Shell",
1866
+ bash: "Shell",
1867
+ dockerfile: "Dockerfile"
1868
+ };
1869
+ async function handleKumaStats(params) {
1870
+ const {
1871
+ filePath,
1872
+ target = "project",
1873
+ outputMode = "rich",
1874
+ compact = false,
1875
+ extensions
1876
+ } = params;
1877
+ if (target === "file" && filePath) {
1878
+ return await statsSingleFile(filePath, { outputMode, compact });
1879
+ }
1880
+ if (target === "dir" && filePath) {
1881
+ return await statsDirectory(filePath, { outputMode, compact, extensions });
1882
+ }
1883
+ return await statsProject({ outputMode, compact, extensions });
1884
+ }
1885
+ async function statsSingleFile(filePath, opts) {
1886
+ const { outputMode, compact } = opts;
1887
+ const validation = validateFilePath(filePath);
1888
+ if (!validation.valid) {
1889
+ return compact ? `ERR:${filePath}` : `Error: ${validation.error.message}`;
1890
+ }
1891
+ const resolvedPath = validation.resolvedPath;
1892
+ if (!fs5.existsSync(resolvedPath)) {
1893
+ return compact ? `ERR:not found` : `Error: File not found: ${filePath}`;
1894
+ }
1895
+ const stat = fs5.statSync(resolvedPath);
1896
+ const isDir = stat.isDirectory();
1897
+ if (isDir) {
1898
+ return statsDirectory(filePath, { outputMode, compact });
1899
+ }
1900
+ const content = fs5.readFileSync(resolvedPath, "utf-8");
1901
+ const lines = content.split("\n");
1902
+ const ext = path6.extname(resolvedPath).replace(".", "").toLowerCase();
1903
+ const lang = LANG_MAP[ext] || ext.toUpperCase() || "Unknown";
1904
+ const totalBytes = stat.size;
1905
+ const nonEmpty = lines.filter((l) => l.trim().length > 0).length;
1906
+ if (outputMode === "json") {
1907
+ return JSON.stringify({
1908
+ file: filePath,
1909
+ lines: lines.length,
1910
+ nonEmpty,
1911
+ size: totalBytes,
1912
+ sizeHuman: formatBytes(totalBytes),
1913
+ language: lang
1914
+ });
1915
+ }
1916
+ if (compact || outputMode === "raw") {
1917
+ return `${filePath} ${lines.length} ${formatBytes(totalBytes)} ${lang}`;
1918
+ }
1919
+ return [
1920
+ `\u{1F4CA} Stats: ${filePath}`,
1921
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
1922
+ ` Language: ${lang}`,
1923
+ ` Lines: ${lines.length}`,
1924
+ ` Non-empty: ${nonEmpty}`,
1925
+ ` Size: ${formatBytes(totalBytes)}`
1926
+ ].join("\n");
1927
+ }
1928
+ async function statsDirectory(dirPath, opts) {
1929
+ const { outputMode, compact, extensions } = opts;
1930
+ const projectRoot = getProjectRoot();
1931
+ const resolved = path6.isAbsolute(dirPath) ? dirPath : path6.join(projectRoot, dirPath);
1932
+ if (!fs5.existsSync(resolved)) {
1933
+ return compact ? `ERR:not found ${dirPath}` : `Error: Directory not found: ${dirPath}`;
1934
+ }
1935
+ let totalFiles = 0;
1936
+ let totalLines = 0;
1937
+ let totalBytes = 0;
1938
+ const langCounts = {};
1939
+ const entries = await fg3("**/*", {
1940
+ cwd: resolved,
1941
+ onlyFiles: true,
1942
+ ignore: [
1943
+ "**/node_modules/**",
1944
+ "**/.git/**",
1945
+ "**/dist/**",
1946
+ "**/build/**",
1947
+ "**/.next/**",
1948
+ "**/coverage/**",
1949
+ "**/*.map"
1950
+ ],
1951
+ deep: 10,
1952
+ dot: false
1953
+ });
1954
+ for (const entry of entries) {
1955
+ const ext = path6.extname(entry).replace(".", "").toLowerCase();
1956
+ if (extensions && extensions.length > 0) {
1957
+ const normalizedExts = extensions.map(
1958
+ (e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
1959
+ );
1960
+ if (!normalizedExts.includes(`.${ext}`)) continue;
1961
+ }
1962
+ totalFiles++;
1963
+ const fullPath = path6.join(resolved, entry);
1964
+ try {
1965
+ const stat = fs5.statSync(fullPath);
1966
+ if (stat.size > 1e6) {
1967
+ totalBytes += stat.size;
1968
+ continue;
1969
+ }
1970
+ const content = fs5.readFileSync(fullPath, "utf-8");
1971
+ const lineCount = content.split("\n").length;
1972
+ totalLines += lineCount;
1973
+ totalBytes += stat.size;
1974
+ const lang = LANG_MAP[ext] || ext.toUpperCase() || "Other";
1975
+ if (!langCounts[lang]) {
1976
+ langCounts[lang] = { files: 0, lines: 0 };
1977
+ }
1978
+ langCounts[lang].files++;
1979
+ langCounts[lang].lines += lineCount;
1980
+ } catch {
1981
+ try {
1982
+ const fallbackStat = fs5.statSync(fullPath);
1983
+ totalBytes += fallbackStat.size;
1984
+ } catch {
1985
+ totalBytes += 0;
1986
+ }
1987
+ }
1988
+ }
1989
+ sessionMemory.recordToolCall("kuma_stats", {
1990
+ target: dirPath,
1991
+ totalFiles,
1992
+ totalLines
1993
+ });
1994
+ if (outputMode === "json") {
1995
+ return JSON.stringify({
1996
+ directory: dirPath,
1997
+ totalFiles,
1998
+ totalLines,
1999
+ totalBytes,
2000
+ sizeHuman: formatBytes(totalBytes),
2001
+ languages: langCounts
2002
+ });
2003
+ }
2004
+ if (compact || outputMode === "raw") {
2005
+ const langs = Object.entries(langCounts).sort((a, b) => b[1].lines - a[1].lines).map(([name, counts]) => `${name} ${counts.files} ${counts.lines}`).join("\n");
2006
+ return `${dirPath} ${totalFiles} ${totalLines} ${formatBytes(totalBytes)}
2007
+ ${langs}`;
2008
+ }
2009
+ const langSummary = Object.entries(langCounts).sort((a, b) => b[1].lines - a[1].lines).slice(0, 10).map(
2010
+ ([name, counts]) => ` ${name.padEnd(20)} ${String(counts.files).padStart(5)} files ${String(counts.lines).padStart(8)} lines`
2011
+ ).join("\n");
2012
+ return [
2013
+ `\u{1F4CA} Directory Stats: ${dirPath}`,
2014
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
2015
+ ` Total files: ${totalFiles}`,
2016
+ ` Total lines: ${totalLines}`,
2017
+ ` Total size: ${formatBytes(totalBytes)}`,
2018
+ ``,
2019
+ `Top Languages:`,
2020
+ langSummary
2021
+ ].join("\n");
2022
+ }
2023
+ async function statsProject(opts) {
2024
+ const projectRoot = getProjectRoot();
2025
+ const projectName = path6.basename(projectRoot);
2026
+ const projectStats = await statsDirectory(projectRoot, opts);
2027
+ let branch = "unknown";
2028
+ let lastCommit = "unknown";
2029
+ try {
2030
+ branch = execSync3("git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown", {
2031
+ cwd: projectRoot,
2032
+ encoding: "utf-8",
2033
+ timeout: 2e3
2034
+ }).trim();
2035
+ lastCommit = execSync3('git log -1 --format="%h %s" 2>/dev/null || echo unknown', {
2036
+ cwd: projectRoot,
2037
+ encoding: "utf-8",
2038
+ timeout: 2e3
2039
+ }).trim();
2040
+ } catch {
2041
+ }
2042
+ if (opts.outputMode === "raw" || opts.compact) {
2043
+ return `Project: ${projectName} Branch: ${branch} ${lastCommit}
2044
+ ${projectStats}`;
2045
+ }
2046
+ return [
2047
+ `\u{1F4CA} Project: ${projectName}`,
2048
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
2049
+ ` Branch: ${branch}`,
2050
+ ` Last commit: ${lastCommit}`,
2051
+ ``,
2052
+ projectStats
2053
+ ].join("\n");
2054
+ }
2055
+ function formatBytes(bytes) {
2056
+ if (bytes === 0) return "0 B";
2057
+ const units = ["B", "KB", "MB", "GB"];
2058
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
2059
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
2060
+ }
2061
+
2062
+ // src/tools/safeTerminalExec.ts
2063
+ import fs7 from "fs";
2064
+ import path8 from "path";
1515
2065
 
1516
2066
  // src/utils/conventionsDetector.ts
1517
- import fs5 from "fs";
1518
- import path5 from "path";
2067
+ import fs6 from "fs";
2068
+ import path7 from "path";
1519
2069
  var cachedConventions = null;
1520
2070
  async function detectConventions(forceRescan = false) {
1521
2071
  if (cachedConventions && !forceRescan) {
@@ -1546,7 +2096,7 @@ async function detectConventions(forceRescan = false) {
1546
2096
  return conventions;
1547
2097
  }
1548
2098
  function detectFramework(root) {
1549
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2099
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1550
2100
  if (pkg) {
1551
2101
  const pkgDeps = pkg.dependencies ?? {};
1552
2102
  const pkgDevDeps = pkg.devDependencies ?? {};
@@ -1591,11 +2141,11 @@ function detectFramework(root) {
1591
2141
  function scanSubProjectFrameworks(root) {
1592
2142
  const frameworks = [];
1593
2143
  try {
1594
- const entries = fs5.readdirSync(root, { withFileTypes: true });
2144
+ const entries = fs6.readdirSync(root, { withFileTypes: true });
1595
2145
  for (const entry of entries) {
1596
2146
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
1597
- const subDir = path5.join(root, entry.name);
1598
- const subPkg = readJsonSafe(path5.join(subDir, "package.json"));
2147
+ const subDir = path7.join(root, entry.name);
2148
+ const subPkg = readJsonSafe(path7.join(subDir, "package.json"));
1599
2149
  if (!subPkg) continue;
1600
2150
  const subDeps = { ...subPkg.dependencies ?? {}, ...subPkg.devDependencies ?? {} };
1601
2151
  if (subDeps.next) frameworks.push("Next.js");
@@ -1614,7 +2164,7 @@ function scanSubProjectFrameworks(root) {
1614
2164
  return frameworks;
1615
2165
  }
1616
2166
  function detectProjectType(root) {
1617
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2167
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1618
2168
  if (pkg) {
1619
2169
  const pkgDeps = pkg.dependencies ?? {};
1620
2170
  const pkgDevDeps = pkg.devDependencies ?? {};
@@ -1638,8 +2188,8 @@ function detectProjectType(root) {
1638
2188
  }
1639
2189
  function detectWorkspaces(root) {
1640
2190
  const results = [];
1641
- const pkg = readJsonSafe(path5.join(root, "package.json"));
1642
- const pnpmWorkspace = readYamlLite(path5.join(root, "pnpm-workspace.yaml"));
2191
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
2192
+ const pnpmWorkspace = readYamlLite(path7.join(root, "pnpm-workspace.yaml"));
1643
2193
  const patterns = /* @__PURE__ */ new Set();
1644
2194
  const pkgWorkspaces = pkg?.workspaces;
1645
2195
  if (Array.isArray(pkgWorkspaces)) {
@@ -1659,22 +2209,22 @@ function detectWorkspaces(root) {
1659
2209
  for (const pattern of patterns) {
1660
2210
  const match = pattern.match(/^([^*]+)\/\*$/);
1661
2211
  if (!match) continue;
1662
- const dir = path5.join(root, match[1]);
1663
- if (!fs5.existsSync(dir)) continue;
2212
+ const dir = path7.join(root, match[1]);
2213
+ if (!fs6.existsSync(dir)) continue;
1664
2214
  let entries = [];
1665
2215
  try {
1666
- entries = fs5.readdirSync(dir, { withFileTypes: true });
2216
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
1667
2217
  } catch {
1668
2218
  continue;
1669
2219
  }
1670
2220
  for (const entry of entries) {
1671
2221
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
1672
- const pkgPath = path5.join(dir, entry.name, "package.json");
2222
+ const pkgPath = path7.join(dir, entry.name, "package.json");
1673
2223
  const subPkg = readJsonSafe(pkgPath);
1674
2224
  if (!subPkg) continue;
1675
- const workspacePath = path5.join(dir, entry.name);
2225
+ const workspacePath = path7.join(dir, entry.name);
1676
2226
  results.push({
1677
- path: path5.relative(root, workspacePath),
2227
+ path: path7.relative(root, workspacePath),
1678
2228
  name: subPkg.name ?? entry.name,
1679
2229
  framework: detectFramework(workspacePath),
1680
2230
  packageManager: detectPackageManagerForDir(workspacePath)
@@ -1690,8 +2240,8 @@ function detectWorkspaces(root) {
1690
2240
  }
1691
2241
  function readYamlLite(filePath) {
1692
2242
  try {
1693
- if (!fs5.existsSync(filePath)) return null;
1694
- const text = fs5.readFileSync(filePath, "utf-8");
2243
+ if (!fs6.existsSync(filePath)) return null;
2244
+ const text = fs6.readFileSync(filePath, "utf-8");
1695
2245
  const packages = [];
1696
2246
  let inPackages = false;
1697
2247
  for (const rawLine of text.split("\n")) {
@@ -1712,7 +2262,7 @@ function readYamlLite(filePath) {
1712
2262
  }
1713
2263
  }
1714
2264
  function detectTestRunner(root) {
1715
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2265
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1716
2266
  if (!pkg) return "unknown";
1717
2267
  const pkgDeps2 = pkg.dependencies ?? {};
1718
2268
  const pkgDevDeps2 = pkg.devDependencies ?? {};
@@ -1733,7 +2283,7 @@ function detectTestRunner(root) {
1733
2283
  return "unknown";
1734
2284
  }
1735
2285
  function detectStyling(root) {
1736
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2286
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1737
2287
  if (!pkg) return "unknown";
1738
2288
  const pkgDeps3 = pkg.dependencies ?? {};
1739
2289
  const pkgDevDeps3 = pkg.devDependencies ?? {};
@@ -1746,22 +2296,22 @@ function detectStyling(root) {
1746
2296
  if (deps.less) return "Less";
1747
2297
  if (deps["@vanilla-extract/css"]) return "Vanilla Extract";
1748
2298
  if (deps.cssmodules || hasCSSModules(root)) return "CSS Modules";
1749
- const srcDir = path5.join(root, "src");
1750
- if (fs5.existsSync(srcDir)) {
2299
+ const srcDir = path7.join(root, "src");
2300
+ if (fs6.existsSync(srcDir)) {
1751
2301
  const cssFiles = findFiles(srcDir, [".css", ".scss", ".less"]);
1752
2302
  if (cssFiles.length > 0) return "Plain CSS/SCSS";
1753
2303
  }
1754
2304
  return "unknown";
1755
2305
  }
1756
2306
  function detectImportAlias(root) {
1757
- const tsconfig = readJsonSafe(path5.join(root, "tsconfig.json"));
2307
+ const tsconfig = readJsonSafe(path7.join(root, "tsconfig.json"));
1758
2308
  const tsconfigPaths = tsconfig?.compilerOptions;
1759
2309
  if (tsconfigPaths?.paths) {
1760
2310
  const paths = tsconfigPaths.paths;
1761
2311
  const alias = Object.keys(paths)[0];
1762
2312
  if (alias) return alias.replace("/*", "");
1763
2313
  }
1764
- const jsconfig = readJsonSafe(path5.join(root, "jsconfig.json"));
2314
+ const jsconfig = readJsonSafe(path7.join(root, "jsconfig.json"));
1765
2315
  const jsconfigPaths = jsconfig?.compilerOptions;
1766
2316
  if (jsconfigPaths?.paths) {
1767
2317
  const paths = jsconfigPaths.paths;
@@ -1772,15 +2322,15 @@ function detectImportAlias(root) {
1772
2322
  }
1773
2323
  function detectLintRules(root) {
1774
2324
  const rules = [];
1775
- if (fs5.existsSync(path5.join(root, ".eslintrc"))) rules.push("ESLint (.eslintrc)");
1776
- if (fs5.existsSync(path5.join(root, ".eslintrc.js"))) rules.push("ESLint (.eslintrc.js)");
1777
- if (fs5.existsSync(path5.join(root, ".eslintrc.json"))) rules.push("ESLint (.eslintrc.json)");
1778
- if (fs5.existsSync(path5.join(root, "eslint.config.js"))) rules.push("ESLint (flat config)");
1779
- if (fs5.existsSync(path5.join(root, ".prettierrc"))) rules.push("Prettier");
1780
- if (fs5.existsSync(path5.join(root, ".prettierrc.json"))) rules.push("Prettier");
1781
- if (fs5.existsSync(path5.join(root, ".prettierrc.js"))) rules.push("Prettier");
1782
- if (fs5.existsSync(path5.join(root, ".stylelintrc"))) rules.push("StyleLint");
1783
- if (fs5.existsSync(path5.join(root, ".stylelintrc.json"))) rules.push("StyleLint");
2325
+ if (fs6.existsSync(path7.join(root, ".eslintrc"))) rules.push("ESLint (.eslintrc)");
2326
+ if (fs6.existsSync(path7.join(root, ".eslintrc.js"))) rules.push("ESLint (.eslintrc.js)");
2327
+ if (fs6.existsSync(path7.join(root, ".eslintrc.json"))) rules.push("ESLint (.eslintrc.json)");
2328
+ if (fs6.existsSync(path7.join(root, "eslint.config.js"))) rules.push("ESLint (flat config)");
2329
+ if (fs6.existsSync(path7.join(root, ".prettierrc"))) rules.push("Prettier");
2330
+ if (fs6.existsSync(path7.join(root, ".prettierrc.json"))) rules.push("Prettier");
2331
+ if (fs6.existsSync(path7.join(root, ".prettierrc.js"))) rules.push("Prettier");
2332
+ if (fs6.existsSync(path7.join(root, ".stylelintrc"))) rules.push("StyleLint");
2333
+ if (fs6.existsSync(path7.join(root, ".stylelintrc.json"))) rules.push("StyleLint");
1784
2334
  return rules;
1785
2335
  }
1786
2336
  function detectPackageManager() {
@@ -1789,26 +2339,26 @@ function detectPackageManager() {
1789
2339
  }
1790
2340
  function detectPackageManagerForDir(dir) {
1791
2341
  const root = getProjectRoot();
1792
- let currentDir = path5.resolve(dir);
2342
+ let currentDir = path7.resolve(dir);
1793
2343
  while (currentDir.startsWith(root)) {
1794
- if (fs5.existsSync(path5.join(currentDir, "pnpm-lock.yaml"))) return "pnpm";
1795
- if (fs5.existsSync(path5.join(currentDir, "yarn.lock"))) return "yarn";
1796
- if (fs5.existsSync(path5.join(currentDir, "package-lock.json"))) return "npm";
1797
- if (fs5.existsSync(path5.join(currentDir, "bun.lockb"))) return "bun";
2344
+ if (fs6.existsSync(path7.join(currentDir, "pnpm-lock.yaml"))) return "pnpm";
2345
+ if (fs6.existsSync(path7.join(currentDir, "yarn.lock"))) return "yarn";
2346
+ if (fs6.existsSync(path7.join(currentDir, "package-lock.json"))) return "npm";
2347
+ if (fs6.existsSync(path7.join(currentDir, "bun.lockb"))) return "bun";
1798
2348
  if (currentDir === root) break;
1799
- currentDir = path5.dirname(currentDir);
2349
+ currentDir = path7.dirname(currentDir);
1800
2350
  }
1801
2351
  return "npm";
1802
2352
  }
1803
2353
  function detectModuleSystem(root) {
1804
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2354
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1805
2355
  if (pkg?.type === "module") return "esm";
1806
- const srcDir = path5.join(root, "src");
1807
- if (fs5.existsSync(srcDir)) {
2356
+ const srcDir = path7.join(root, "src");
2357
+ if (fs6.existsSync(srcDir)) {
1808
2358
  const tsFiles = findFiles(srcDir, [".ts", ".tsx", ".js", ".jsx", ".mjs"]);
1809
2359
  for (const file of tsFiles.slice(0, 20)) {
1810
2360
  try {
1811
- const content = fs5.readFileSync(file, "utf-8");
2361
+ const content = fs6.readFileSync(file, "utf-8");
1812
2362
  if (content.includes("import ") || content.includes("export ")) return "esm";
1813
2363
  } catch {
1814
2364
  continue;
@@ -1818,22 +2368,22 @@ function detectModuleSystem(root) {
1818
2368
  return "cjs";
1819
2369
  }
1820
2370
  function detectLanguage(root) {
1821
- if (fs5.existsSync(path5.join(root, "go.mod"))) return "go";
1822
- if (fs5.existsSync(path5.join(root, "Cargo.toml"))) return "rust";
1823
- if (fs5.existsSync(path5.join(root, "composer.json"))) return "php";
1824
- if (fs5.existsSync(path5.join(root, "pyproject.toml")) || fs5.existsSync(path5.join(root, "requirements.txt")) || fs5.existsSync(path5.join(root, "setup.py"))) return "python";
1825
- if (fs5.existsSync(path5.join(root, "Gemfile"))) return "ruby";
1826
- if (fs5.existsSync(path5.join(root, "pom.xml"))) return "java";
1827
- if (fs5.existsSync(path5.join(root, "build.gradle")) || fs5.existsSync(path5.join(root, "build.gradle.kts"))) return "kotlin";
1828
- if (fs5.existsSync(path5.join(root, "Package.swift"))) return "swift";
2371
+ if (fs6.existsSync(path7.join(root, "go.mod"))) return "go";
2372
+ if (fs6.existsSync(path7.join(root, "Cargo.toml"))) return "rust";
2373
+ if (fs6.existsSync(path7.join(root, "composer.json"))) return "php";
2374
+ if (fs6.existsSync(path7.join(root, "pyproject.toml")) || fs6.existsSync(path7.join(root, "requirements.txt")) || fs6.existsSync(path7.join(root, "setup.py"))) return "python";
2375
+ if (fs6.existsSync(path7.join(root, "Gemfile"))) return "ruby";
2376
+ if (fs6.existsSync(path7.join(root, "pom.xml"))) return "java";
2377
+ if (fs6.existsSync(path7.join(root, "build.gradle")) || fs6.existsSync(path7.join(root, "build.gradle.kts"))) return "kotlin";
2378
+ if (fs6.existsSync(path7.join(root, "Package.swift"))) return "swift";
1829
2379
  try {
1830
- const entries = fs5.readdirSync(root);
2380
+ const entries = fs6.readdirSync(root);
1831
2381
  if (entries.some((e) => e.endsWith(".csproj"))) return "csharp";
1832
2382
  } catch {
1833
2383
  }
1834
- if (fs5.existsSync(path5.join(root, "tsconfig.json"))) return "typescript";
1835
- const srcDir = path5.join(root, "src");
1836
- if (fs5.existsSync(srcDir)) {
2384
+ if (fs6.existsSync(path7.join(root, "tsconfig.json"))) return "typescript";
2385
+ const srcDir = path7.join(root, "src");
2386
+ if (fs6.existsSync(srcDir)) {
1837
2387
  const tsFiles = findFiles(srcDir, [".ts", ".tsx"]);
1838
2388
  if (tsFiles.length > 0) return "typescript";
1839
2389
  const jsFiles = findFiles(srcDir, [".js", ".jsx"]);
@@ -1852,7 +2402,7 @@ function detectMultiLanguageFramework(root, language) {
1852
2402
  switch (language) {
1853
2403
  case "go": {
1854
2404
  try {
1855
- const goMod = fs5.readFileSync(path5.join(root, "go.mod"), "utf-8");
2405
+ const goMod = fs6.readFileSync(path7.join(root, "go.mod"), "utf-8");
1856
2406
  if (goMod.includes("github.com/gin-gonic/gin")) return "Gin";
1857
2407
  if (goMod.includes("github.com/labstack/echo")) return "Echo";
1858
2408
  if (goMod.includes("github.com/gofiber/fiber")) return "Fiber";
@@ -1866,7 +2416,7 @@ function detectMultiLanguageFramework(root, language) {
1866
2416
  }
1867
2417
  case "php": {
1868
2418
  try {
1869
- const composer = JSON.parse(fs5.readFileSync(path5.join(root, "composer.json"), "utf-8"));
2419
+ const composer = JSON.parse(fs6.readFileSync(path7.join(root, "composer.json"), "utf-8"));
1870
2420
  const req = composer.require ?? {};
1871
2421
  const reqDev = composer["require-dev"] ?? {};
1872
2422
  const deps = { ...req, ...reqDev };
@@ -1883,14 +2433,14 @@ function detectMultiLanguageFramework(root, language) {
1883
2433
  }
1884
2434
  case "python": {
1885
2435
  try {
1886
- if (fs5.existsSync(path5.join(root, "pyproject.toml"))) {
1887
- const content = fs5.readFileSync(path5.join(root, "pyproject.toml"), "utf-8");
2436
+ if (fs6.existsSync(path7.join(root, "pyproject.toml"))) {
2437
+ const content = fs6.readFileSync(path7.join(root, "pyproject.toml"), "utf-8");
1888
2438
  if (content.includes("django")) return "Django";
1889
2439
  if (content.includes("flask")) return "Flask";
1890
2440
  if (content.includes("fastapi")) return "FastAPI";
1891
2441
  }
1892
- if (fs5.existsSync(path5.join(root, "requirements.txt"))) {
1893
- const content = fs5.readFileSync(path5.join(root, "requirements.txt"), "utf-8");
2442
+ if (fs6.existsSync(path7.join(root, "requirements.txt"))) {
2443
+ const content = fs6.readFileSync(path7.join(root, "requirements.txt"), "utf-8");
1894
2444
  if (content.includes("django")) return "Django";
1895
2445
  if (content.includes("flask")) return "Flask";
1896
2446
  if (content.includes("fastapi")) return "FastAPI";
@@ -1903,7 +2453,7 @@ function detectMultiLanguageFramework(root, language) {
1903
2453
  }
1904
2454
  case "rust": {
1905
2455
  try {
1906
- const cargo = fs5.readFileSync(path5.join(root, "Cargo.toml"), "utf-8");
2456
+ const cargo = fs6.readFileSync(path7.join(root, "Cargo.toml"), "utf-8");
1907
2457
  if (cargo.includes("axum")) return "Axum";
1908
2458
  if (cargo.includes("actix-web")) return "Actix Web";
1909
2459
  if (cargo.includes("rocket")) return "Rocket";
@@ -1917,7 +2467,7 @@ function detectMultiLanguageFramework(root, language) {
1917
2467
  }
1918
2468
  case "java": {
1919
2469
  try {
1920
- const content = fs5.readFileSync(path5.join(root, "pom.xml"), "utf-8");
2470
+ const content = fs6.readFileSync(path7.join(root, "pom.xml"), "utf-8");
1921
2471
  if (content.includes("spring-boot")) return "Spring Boot";
1922
2472
  if (content.includes("quarkus")) return "Quarkus";
1923
2473
  if (content.includes("micronaut")) return "Micronaut";
@@ -1925,7 +2475,7 @@ function detectMultiLanguageFramework(root, language) {
1925
2475
  if (content.includes("helidon")) return "Helidon";
1926
2476
  } catch {
1927
2477
  try {
1928
- const gradle = fs5.readFileSync(path5.join(root, "build.gradle"), "utf-8");
2478
+ const gradle = fs6.readFileSync(path7.join(root, "build.gradle"), "utf-8");
1929
2479
  if (gradle.includes("spring")) return "Spring Boot";
1930
2480
  if (gradle.includes("quarkus")) return "Quarkus";
1931
2481
  } catch {
@@ -1935,7 +2485,7 @@ function detectMultiLanguageFramework(root, language) {
1935
2485
  }
1936
2486
  case "kotlin": {
1937
2487
  try {
1938
- const gradle = fs5.readFileSync(path5.join(root, "build.gradle.kts"), "utf-8");
2488
+ const gradle = fs6.readFileSync(path7.join(root, "build.gradle.kts"), "utf-8");
1939
2489
  if (gradle.includes("spring")) return "Spring Boot (Kotlin)";
1940
2490
  if (gradle.includes("ktor")) return "Ktor";
1941
2491
  } catch {
@@ -1944,7 +2494,7 @@ function detectMultiLanguageFramework(root, language) {
1944
2494
  }
1945
2495
  case "ruby": {
1946
2496
  try {
1947
- const gemfile = fs5.readFileSync(path5.join(root, "Gemfile"), "utf-8");
2497
+ const gemfile = fs6.readFileSync(path7.join(root, "Gemfile"), "utf-8");
1948
2498
  if (gemfile.includes("rails")) return "Ruby on Rails";
1949
2499
  if (gemfile.includes("sinatra")) return "Sinatra";
1950
2500
  if (gemfile.includes("hanami")) return "Hanami";
@@ -1955,10 +2505,10 @@ function detectMultiLanguageFramework(root, language) {
1955
2505
  }
1956
2506
  case "csharp": {
1957
2507
  try {
1958
- const entries = fs5.readdirSync(root);
2508
+ const entries = fs6.readdirSync(root);
1959
2509
  const csproj = entries.find((e) => e.endsWith(".csproj"));
1960
2510
  if (csproj) {
1961
- const content = fs5.readFileSync(path5.join(root, csproj), "utf-8");
2511
+ const content = fs6.readFileSync(path7.join(root, csproj), "utf-8");
1962
2512
  if (content.includes("Microsoft.AspNetCore")) return "ASP.NET Core";
1963
2513
  if (content.includes("Microsoft.Maui")) return ".NET MAUI";
1964
2514
  if (content.includes("Serilog")) return "Serilog";
@@ -1969,7 +2519,7 @@ function detectMultiLanguageFramework(root, language) {
1969
2519
  }
1970
2520
  case "swift": {
1971
2521
  try {
1972
- const content = fs5.readFileSync(path5.join(root, "Package.swift"), "utf-8");
2522
+ const content = fs6.readFileSync(path7.join(root, "Package.swift"), "utf-8");
1973
2523
  if (content.includes("vapor")) return "Vapor";
1974
2524
  if (content.includes("swift-nio")) return "SwiftNIO";
1975
2525
  if (content.includes("perfect")) return "Perfect";
@@ -1984,7 +2534,7 @@ function detectMultiLanguageFramework(root, language) {
1984
2534
  }
1985
2535
  function detectFeatures(root) {
1986
2536
  const features = [];
1987
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2537
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1988
2538
  if (!pkg) return features;
1989
2539
  const pkgDeps4 = pkg.dependencies ?? {};
1990
2540
  const pkgDevDeps4 = pkg.devDependencies ?? {};
@@ -2008,25 +2558,25 @@ function detectFeatures(root) {
2008
2558
  }
2009
2559
  function readJsonSafe(filePath) {
2010
2560
  try {
2011
- if (fs5.existsSync(filePath)) {
2012
- return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
2561
+ if (fs6.existsSync(filePath)) {
2562
+ return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
2013
2563
  }
2014
2564
  } catch {
2015
2565
  }
2016
2566
  return null;
2017
2567
  }
2018
2568
  function hasCSSModules(root) {
2019
- const srcDir = path5.join(root, "src");
2020
- if (!fs5.existsSync(srcDir)) return false;
2569
+ const srcDir = path7.join(root, "src");
2570
+ if (!fs6.existsSync(srcDir)) return false;
2021
2571
  const files = findFiles(srcDir, [".module.css", ".module.scss", ".module.less"]);
2022
2572
  return files.length > 0;
2023
2573
  }
2024
2574
  function findFiles(dir, extensions) {
2025
2575
  const results = [];
2026
2576
  try {
2027
- const entries = fs5.readdirSync(dir, { withFileTypes: true });
2577
+ const entries = fs6.readdirSync(dir, { withFileTypes: true });
2028
2578
  for (const entry of entries) {
2029
- const fullPath = path5.join(dir, entry.name);
2579
+ const fullPath = path7.join(dir, entry.name);
2030
2580
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
2031
2581
  results.push(...findFiles(fullPath, extensions));
2032
2582
  } else if (entry.isFile()) {
@@ -2217,7 +2767,7 @@ async function handleSafeTerminalExec(params) {
2217
2767
  const workspaces = conventions?.workspaces;
2218
2768
  const matched = workspaces?.find((w) => w.name === workspace || w.path === workspace);
2219
2769
  if (matched) {
2220
- workingDir = path6.resolve(projectRoot, matched.path);
2770
+ workingDir = path8.resolve(projectRoot, matched.path);
2221
2771
  resolvedFrom = `workspace "${matched.name}" \u2192 ${matched.path}`;
2222
2772
  } else {
2223
2773
  return `\u26A0\uFE0F Workspace "${workspace}" not found. Run project_conventions first to detect workspaces, or use 'cwd' parameter with a direct path.
@@ -2225,13 +2775,13 @@ async function handleSafeTerminalExec(params) {
2225
2775
  Available workspaces: ${workspaces?.map((w) => `"${w.name}" (${w.path})`).join(", ") || "none detected"}`;
2226
2776
  }
2227
2777
  } else if (inputCwd) {
2228
- const resolved = path6.resolve(projectRoot, inputCwd);
2229
- const normalizedResolved = path6.normalize(resolved).toLowerCase();
2230
- const normalizedRoot = path6.normalize(projectRoot).toLowerCase();
2778
+ const resolved = path8.resolve(projectRoot, inputCwd);
2779
+ const normalizedResolved = path8.normalize(resolved).toLowerCase();
2780
+ const normalizedRoot = path8.normalize(projectRoot).toLowerCase();
2231
2781
  if (!normalizedResolved.startsWith(normalizedRoot)) {
2232
2782
  return `\u{1F6AB} BLOCKED: Path "${inputCwd}" resolves outside project root "${projectRoot}".`;
2233
2783
  }
2234
- if (!fs6.existsSync(resolved)) {
2784
+ if (!fs7.existsSync(resolved)) {
2235
2785
  return `\u26A0\uFE0F Directory "${inputCwd}" does not exist at "${resolved}".`;
2236
2786
  }
2237
2787
  workingDir = resolved;
@@ -2317,9 +2867,9 @@ function formatTimeoutResult(command, timeout) {
2317
2867
  }
2318
2868
 
2319
2869
  // src/agents/codeReviewer.ts
2320
- import fs7 from "fs";
2321
- import path7 from "path";
2322
- import { execSync as execSync2 } from "child_process";
2870
+ import fs8 from "fs";
2871
+ import path9 from "path";
2872
+ import { execSync as execSync4 } from "child_process";
2323
2873
  var CODE_EXTENSIONS = [
2324
2874
  ".ts",
2325
2875
  ".tsx",
@@ -2349,7 +2899,7 @@ var CONFIG_EXTENSIONS = [
2349
2899
  function getGitChangedFiles() {
2350
2900
  try {
2351
2901
  const root = getProjectRoot();
2352
- const stdout = execSync2("git status --porcelain", {
2902
+ const stdout = execSync4("git status --porcelain", {
2353
2903
  cwd: root,
2354
2904
  encoding: "utf-8"
2355
2905
  });
@@ -2362,7 +2912,7 @@ function getGitChangedFiles() {
2362
2912
  if (filePath.startsWith('"') && filePath.endsWith('"')) {
2363
2913
  filePath = filePath.substring(1, filePath.length - 1);
2364
2914
  }
2365
- const ext = path7.extname(filePath).toLowerCase();
2915
+ const ext = path9.extname(filePath).toLowerCase();
2366
2916
  if (CODE_EXTENSIONS.includes(ext)) files.push(filePath);
2367
2917
  }
2368
2918
  return files.slice(0, 10);
@@ -2399,7 +2949,7 @@ Pass an explicit 'files' array to review specific files.`;
2399
2949
  continue;
2400
2950
  }
2401
2951
  const resolvedPath = validation.resolvedPath;
2402
- if (!fs7.existsSync(resolvedPath)) {
2952
+ if (!fs8.existsSync(resolvedPath)) {
2403
2953
  allIssues.push({
2404
2954
  file: filePath,
2405
2955
  line: 0,
@@ -2411,7 +2961,7 @@ Pass an explicit 'files' array to review specific files.`;
2411
2961
  continue;
2412
2962
  }
2413
2963
  try {
2414
- const content = fs7.readFileSync(resolvedPath, "utf-8");
2964
+ const content = fs8.readFileSync(resolvedPath, "utf-8");
2415
2965
  filesReviewed++;
2416
2966
  checkGeneral(filePath, content, allIssues);
2417
2967
  switch (focus) {
@@ -2509,12 +3059,12 @@ function isTestFile(filePath) {
2509
3059
  return /\.(test|spec)\.[a-z]+$/i.test(filePath) || /__tests__/.test(filePath);
2510
3060
  }
2511
3061
  function isTsLike(filePath) {
2512
- const ext = path7.extname(filePath).toLowerCase();
3062
+ const ext = path9.extname(filePath).toLowerCase();
2513
3063
  return ext === ".ts" || ext === ".tsx";
2514
3064
  }
2515
3065
  function checkGeneral(filePath, content, issues) {
2516
3066
  const lines = content.split("\n");
2517
- const ext = path7.extname(filePath).toLowerCase();
3067
+ const ext = path9.extname(filePath).toLowerCase();
2518
3068
  if (content.length > 0 && !content.endsWith("\n")) {
2519
3069
  issues.push({
2520
3070
  file: filePath,
@@ -2716,7 +3266,7 @@ function stripJsonComments(text) {
2716
3266
  return cleaned.join("\n");
2717
3267
  }
2718
3268
  function checkConfigFile(filePath, content, issues) {
2719
- const ext = path7.extname(filePath).toLowerCase();
3269
+ const ext = path9.extname(filePath).toLowerCase();
2720
3270
  const lines = content.split("\n");
2721
3271
  if (ext === ".json" || ext === ".jsonc") {
2722
3272
  checkJson(filePath, content, lines, issues);
@@ -2727,7 +3277,7 @@ function checkConfigFile(filePath, content, issues) {
2727
3277
  }
2728
3278
  }
2729
3279
  function checkJson(filePath, content, lines, issues) {
2730
- const ext = path7.extname(filePath).toLowerCase();
3280
+ const ext = path9.extname(filePath).toLowerCase();
2731
3281
  const isJsonc = ext === ".jsonc";
2732
3282
  const contentToParse = isJsonc ? stripJsonComments(content) : content;
2733
3283
  try {
@@ -3453,10 +4003,10 @@ function formatConventionsOutput(conventions) {
3453
4003
  }
3454
4004
 
3455
4005
  // src/utils/gitUtils.ts
3456
- import { execSync as execSync3 } from "child_process";
4006
+ import { execSync as execSync5 } from "child_process";
3457
4007
  function runGitCommand(command) {
3458
4008
  const root = getProjectRoot();
3459
- return execSync3(command, {
4009
+ return execSync5(command, {
3460
4010
  cwd: root,
3461
4011
  encoding: "utf-8",
3462
4012
  maxBuffer: 2 * 1024 * 1024
@@ -3584,8 +4134,8 @@ async function handleGitDiff(params) {
3584
4134
  }
3585
4135
 
3586
4136
  // src/tools/projectStructure.ts
3587
- import fs8 from "fs";
3588
- import path8 from "path";
4137
+ import fs9 from "fs";
4138
+ import path10 from "path";
3589
4139
  var DEFAULT_IGNORE = [
3590
4140
  "node_modules",
3591
4141
  ".git",
@@ -3603,7 +4153,7 @@ var DEFAULT_IGNORE = [
3603
4153
  "*.log"
3604
4154
  ];
3605
4155
  var treeCache = /* @__PURE__ */ new Map();
3606
- var CACHE_TTL_MS2 = 6e4;
4156
+ var CACHE_TTL_MS3 = 6e4;
3607
4157
  async function handleProjectStructure(params) {
3608
4158
  const {
3609
4159
  depth = 3,
@@ -3615,14 +4165,14 @@ async function handleProjectStructure(params) {
3615
4165
  const clampedDepth = Math.max(1, Math.min(depth, 6));
3616
4166
  const cacheKey = `${root}:${clampedDepth}:${folderOnly}:${includePattern ?? ""}:${excludePattern ?? ""}`;
3617
4167
  const cached = treeCache.get(cacheKey);
3618
- if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS2) {
4168
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS3) {
3619
4169
  sessionMemory.recordToolCall("project_structure", { depth: clampedDepth, folderOnly, cached: true });
3620
4170
  return cached.result;
3621
4171
  }
3622
4172
  sessionMemory.recordToolCall("project_structure", { depth: clampedDepth, folderOnly });
3623
4173
  try {
3624
4174
  const tree = buildTree(root, root, clampedDepth, 0, folderOnly, includePattern, excludePattern);
3625
- const projectName = path8.basename(root);
4175
+ const projectName = path10.basename(root);
3626
4176
  const lines = [
3627
4177
  "[Project Structure] - " + projectName,
3628
4178
  "Depth: " + clampedDepth + " | " + (folderOnly ? "Folders only" : "Files and folders"),
@@ -3645,7 +4195,7 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3645
4195
  const lines = [];
3646
4196
  let entries = [];
3647
4197
  try {
3648
- entries = fs8.readdirSync(currentDir, { withFileTypes: true });
4198
+ entries = fs9.readdirSync(currentDir, { withFileTypes: true });
3649
4199
  } catch {
3650
4200
  return lines;
3651
4201
  }
@@ -3654,12 +4204,12 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3654
4204
  if (!a.isDirectory() && b.isDirectory()) return 1;
3655
4205
  return a.name.localeCompare(b.name);
3656
4206
  });
3657
- const relativeDir = path8.relative(root, currentDir) || ".";
4207
+ const relativeDir = path10.relative(root, currentDir) || ".";
3658
4208
  const prefix = getPrefix(currentDepth);
3659
4209
  for (const entry of entries) {
3660
4210
  if (shouldIgnore(entry.name, relativeDir, root)) continue;
3661
- const fullPath = path8.join(currentDir, entry.name);
3662
- const relativePath = path8.relative(root, fullPath);
4211
+ const fullPath = path10.join(currentDir, entry.name);
4212
+ const relativePath = path10.relative(root, fullPath);
3663
4213
  if (includePattern && !entry.name.includes(includePattern) && !relativePath.includes(includePattern)) {
3664
4214
  if (!entry.isDirectory()) continue;
3665
4215
  }
@@ -3670,11 +4220,11 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3670
4220
  lines.push(prefix + "[D] " + entry.name + "/");
3671
4221
  let subEntries = [];
3672
4222
  try {
3673
- subEntries = fs8.readdirSync(fullPath, { withFileTypes: true });
4223
+ subEntries = fs9.readdirSync(fullPath, { withFileTypes: true });
3674
4224
  } catch {
3675
4225
  }
3676
4226
  const hasVisibleContent = subEntries.some(
3677
- (e) => !shouldIgnore(e.name, path8.relative(root, fullPath), root)
4227
+ (e) => !shouldIgnore(e.name, path10.relative(root, fullPath), root)
3678
4228
  );
3679
4229
  if (hasVisibleContent) {
3680
4230
  const subLines = buildTree(root, fullPath, maxDepth, currentDepth + 1, folderOnly, includePattern, excludePattern);
@@ -3705,7 +4255,7 @@ function getPrefix(depth) {
3705
4255
  }
3706
4256
  function getFileSize(fullPath) {
3707
4257
  try {
3708
- const stat = fs8.statSync(fullPath);
4258
+ const stat = fs9.statSync(fullPath);
3709
4259
  if (stat.size < 1024) return stat.size + "B";
3710
4260
  if (stat.size < 1024 * 1024) return (stat.size / 1024).toFixed(0) + "KB";
3711
4261
  const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
@@ -3716,8 +4266,8 @@ function getFileSize(fullPath) {
3716
4266
  }
3717
4267
 
3718
4268
  // src/tools/staticAnalysis.ts
3719
- import fs9 from "fs";
3720
- import path9 from "path";
4269
+ import fs10 from "fs";
4270
+ import path11 from "path";
3721
4271
  async function handleStaticAnalysis(params) {
3722
4272
  const { tool = "all", files, autoFix = false, timeout = 60 } = params;
3723
4273
  const root = getProjectRoot();
@@ -3810,11 +4360,11 @@ function detectAvailableTools(root) {
3810
4360
  "eslint.config.js",
3811
4361
  "eslint.config.mjs"
3812
4362
  ];
3813
- const hasEslintConfig = eslintConfigs.some((cfg) => fs9.existsSync(path9.join(root, cfg)));
4363
+ const hasEslintConfig = eslintConfigs.some((cfg) => fs10.existsSync(path11.join(root, cfg)));
3814
4364
  if (hasEslintConfig && depNames.has("eslint")) {
3815
4365
  tools.push("eslint");
3816
4366
  }
3817
- if (fs9.existsSync(path9.join(root, "tsconfig.json")) && depNames.has("typescript")) {
4367
+ if (fs10.existsSync(path11.join(root, "tsconfig.json")) && depNames.has("typescript")) {
3818
4368
  tools.push("tsc");
3819
4369
  }
3820
4370
  const prettierConfigs = [
@@ -3825,20 +4375,20 @@ function detectAvailableTools(root) {
3825
4375
  ".prettierrc.toml",
3826
4376
  "prettier.config.js"
3827
4377
  ];
3828
- const hasPrettierConfig = prettierConfigs.some((cfg) => fs9.existsSync(path9.join(root, cfg)));
4378
+ const hasPrettierConfig = prettierConfigs.some((cfg) => fs10.existsSync(path11.join(root, cfg)));
3829
4379
  if (hasPrettierConfig && depNames.has("prettier")) {
3830
4380
  tools.push("prettier");
3831
4381
  }
3832
- if (fs9.existsSync(path9.join(root, "ruff.toml")) || fs9.existsSync(path9.join(root, ".ruff.toml"))) {
4382
+ if (fs10.existsSync(path11.join(root, "ruff.toml")) || fs10.existsSync(path11.join(root, ".ruff.toml"))) {
3833
4383
  tools.push("ruff");
3834
4384
  }
3835
4385
  return tools;
3836
4386
  }
3837
4387
  function readPackageJson(root) {
3838
4388
  try {
3839
- const pkgPath = path9.join(root, "package.json");
3840
- if (fs9.existsSync(pkgPath)) {
3841
- return JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
4389
+ const pkgPath = path11.join(root, "package.json");
4390
+ if (fs10.existsSync(pkgPath)) {
4391
+ return JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
3842
4392
  }
3843
4393
  } catch {
3844
4394
  }
@@ -3894,18 +4444,18 @@ function buildToolCommand(tool, files, autoFix) {
3894
4444
  }
3895
4445
  function detectPackageManagerPrefix() {
3896
4446
  const root = getProjectRoot();
3897
- if (fs9.existsSync(path9.join(root, "pnpm-lock.yaml"))) return "pnpm ";
3898
- if (fs9.existsSync(path9.join(root, "yarn.lock"))) return "yarn ";
4447
+ if (fs10.existsSync(path11.join(root, "pnpm-lock.yaml"))) return "pnpm ";
4448
+ if (fs10.existsSync(path11.join(root, "yarn.lock"))) return "yarn ";
3899
4449
  return "npx --no-install ";
3900
4450
  }
3901
4451
  function findBinary(root, binName) {
3902
4452
  const candidates = [
3903
- path9.join(root, "node_modules", ".bin", binName),
3904
- path9.join(root, "..", "node_modules", ".bin", binName)
4453
+ path11.join(root, "node_modules", ".bin", binName),
4454
+ path11.join(root, "..", "node_modules", ".bin", binName)
3905
4455
  ];
3906
4456
  for (const candidate of candidates) {
3907
4457
  try {
3908
- if (fs9.existsSync(candidate)) {
4458
+ if (fs10.existsSync(candidate)) {
3909
4459
  return candidate;
3910
4460
  }
3911
4461
  } catch {
@@ -3929,7 +4479,7 @@ function parseToolOutput(tool, stdout, stderr, projectRoot) {
3929
4479
  }
3930
4480
  function resolveToolPath(filePath, projectRoot) {
3931
4481
  const trimmed = filePath.trim();
3932
- return path9.isAbsolute(trimmed) ? path9.relative(projectRoot, trimmed) : trimmed.replace(/^\.\//, "");
4482
+ return path11.isAbsolute(trimmed) ? path11.relative(projectRoot, trimmed) : trimmed.replace(/^\.\//, "");
3933
4483
  }
3934
4484
  function parseEslintOutput(output, projectRoot) {
3935
4485
  const issues = [];
@@ -4112,7 +4662,7 @@ function formatToolNotAvailable(requested, available) {
4112
4662
  }
4113
4663
 
4114
4664
  // src/utils/kumaShared.ts
4115
- import { execSync as execSync4 } from "child_process";
4665
+ import { execSync as execSync6 } from "child_process";
4116
4666
  function getSessionStats(inputGoal) {
4117
4667
  const summary = sessionMemory.getSummary();
4118
4668
  const goal = inputGoal || summary.currentGoal || "";
@@ -4134,7 +4684,7 @@ function getSessionStats(inputGoal) {
4134
4684
  function getGitDiffStat(timeout = 3e3) {
4135
4685
  try {
4136
4686
  const root = getProjectRoot();
4137
- return execSync4("git diff --stat", {
4687
+ return execSync6("git diff --stat", {
4138
4688
  cwd: root,
4139
4689
  encoding: "utf-8",
4140
4690
  timeout
@@ -4407,8 +4957,8 @@ function formatAnalytics(a) {
4407
4957
  }
4408
4958
 
4409
4959
  // src/engine/kumaHealthDashboard.ts
4410
- import fs10 from "fs";
4411
- import path10 from "path";
4960
+ import fs11 from "fs";
4961
+ import path12 from "path";
4412
4962
  function computeHealthDashboard() {
4413
4963
  const analytics = computeAnalytics();
4414
4964
  const root = getProjectRoot();
@@ -4416,27 +4966,27 @@ function computeHealthDashboard() {
4416
4966
  const gitChanges = gitStat ? gitStat.split("\n").filter((l) => l.includes("|")).length : 0;
4417
4967
  let backupCount = 0;
4418
4968
  try {
4419
- const bd = path10.join(root, ".kuma", "backups");
4420
- if (fs10.existsSync(bd)) backupCount = fs10.readdirSync(bd).filter((d) => /^\d+$/.test(d)).length;
4969
+ const bd = path12.join(root, ".kuma", "backups");
4970
+ if (fs11.existsSync(bd)) backupCount = fs11.readdirSync(bd).filter((d) => /^\d+$/.test(d)).length;
4421
4971
  } catch {
4422
4972
  }
4423
4973
  const dirFileCounts = /* @__PURE__ */ new Map();
4424
4974
  try {
4425
4975
  const walk = (dir, base) => {
4426
4976
  try {
4427
- for (const e of fs10.readdirSync(dir, { withFileTypes: true })) {
4977
+ for (const e of fs11.readdirSync(dir, { withFileTypes: true })) {
4428
4978
  if (e.name.startsWith(".") || e.name === "node_modules") continue;
4429
- const full = path10.join(dir, e.name);
4979
+ const full = path12.join(dir, e.name);
4430
4980
  if (e.isDirectory()) walk(full, base);
4431
4981
  else if (e.isFile() && /\.(ts|tsx|js|jsx)$/.test(e.name)) {
4432
- const rel = path10.dirname(path10.relative(base, full));
4982
+ const rel = path12.dirname(path12.relative(base, full));
4433
4983
  dirFileCounts.set(rel, (dirFileCounts.get(rel) || 0) + 1);
4434
4984
  }
4435
4985
  }
4436
4986
  } catch {
4437
4987
  }
4438
4988
  };
4439
- walk(path10.join(root, "src"), root);
4989
+ walk(path12.join(root, "src"), root);
4440
4990
  } catch {
4441
4991
  }
4442
4992
  const dirMap = /* @__PURE__ */ new Map();
@@ -4444,7 +4994,7 @@ function computeHealthDashboard() {
4444
4994
  if (call.toolName === "precise_diff_editor") {
4445
4995
  const fp = call.params?.filePath;
4446
4996
  if (fp) {
4447
- const dir = path10.dirname(fp) || ".";
4997
+ const dir = path12.dirname(fp) || ".";
4448
4998
  const e = dirMap.get(dir) || { edits: 0, failures: 0 };
4449
4999
  e.edits++;
4450
5000
  if (call.params?.success === false) e.failures++;
@@ -4524,9 +5074,9 @@ function formatHealthDashboard(r) {
4524
5074
  }
4525
5075
 
4526
5076
  // src/guards/antiPatternDetector.ts
4527
- import fs11 from "fs";
4528
- import path11 from "path";
4529
- import { execSync as execSync5 } from "child_process";
5077
+ import fs12 from "fs";
5078
+ import path13 from "path";
5079
+ import { execSync as execSync7 } from "child_process";
4530
5080
  var SCRIPT_PATCH_PATTERNS = [
4531
5081
  "writeFileSync",
4532
5082
  "writeFile",
@@ -4552,20 +5102,20 @@ function findPatchScripts(projectRoot) {
4552
5102
  const warnings = [];
4553
5103
  const recentFiles = scanRecentFiles(projectRoot);
4554
5104
  for (const file of recentFiles) {
4555
- const ext = path11.extname(file).toLowerCase();
5105
+ const ext = path13.extname(file).toLowerCase();
4556
5106
  if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
4557
- const relativePath = path11.relative(projectRoot, file);
5107
+ const relativePath = path13.relative(projectRoot, file);
4558
5108
  if (relativePath.startsWith("src") || relativePath.startsWith("test") || relativePath.startsWith("node_modules") || relativePath.startsWith(".")) {
4559
5109
  continue;
4560
5110
  }
4561
5111
  try {
4562
- const content = fs11.readFileSync(file, "utf-8").toLowerCase();
5112
+ const content = fs12.readFileSync(file, "utf-8").toLowerCase();
4563
5113
  const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
4564
5114
  if (matchedPattern) {
4565
5115
  warnings.push({
4566
5116
  severity: "high",
4567
5117
  pattern: "script-patching",
4568
- message: `Created script file that modifies other files: ${path11.basename(file)}`,
5118
+ message: `Created script file that modifies other files: ${path13.basename(file)}`,
4569
5119
  suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
4570
5120
  evidence: `File: ${relativePath} contains '${matchedPattern}'`,
4571
5121
  filePath: relativePath
@@ -4599,15 +5149,15 @@ function detectBashGrepUsage() {
4599
5149
  function scanRecentFiles(projectRoot) {
4600
5150
  const recent = [];
4601
5151
  try {
4602
- const entries = fs11.readdirSync(projectRoot, { withFileTypes: true });
5152
+ const entries = fs12.readdirSync(projectRoot, { withFileTypes: true });
4603
5153
  const now = Date.now();
4604
5154
  const recentThreshold = 30 * 60 * 1e3;
4605
5155
  for (const entry of entries) {
4606
5156
  if (!entry.isFile()) continue;
4607
5157
  try {
4608
- const stat = fs11.statSync(path11.join(projectRoot, entry.name));
5158
+ const stat = fs12.statSync(path13.join(projectRoot, entry.name));
4609
5159
  if (now - stat.mtimeMs < recentThreshold || now - stat.ctimeMs < recentThreshold) {
4610
- recent.push(path11.join(projectRoot, entry.name));
5160
+ recent.push(path13.join(projectRoot, entry.name));
4611
5161
  }
4612
5162
  } catch {
4613
5163
  continue;
@@ -4620,7 +5170,7 @@ function scanRecentFiles(projectRoot) {
4620
5170
  function detectGitPatchScripts(projectRoot) {
4621
5171
  const warnings = [];
4622
5172
  try {
4623
- const statusStdout = execSync5("git status --porcelain", {
5173
+ const statusStdout = execSync7("git status --porcelain", {
4624
5174
  cwd: projectRoot,
4625
5175
  encoding: "utf-8",
4626
5176
  timeout: 3e3
@@ -4631,14 +5181,14 @@ function detectGitPatchScripts(projectRoot) {
4631
5181
  const prefix = line.substring(0, 2);
4632
5182
  if (prefix !== "??" && prefix !== "A ") continue;
4633
5183
  const file = line.substring(3).trim();
4634
- const ext = path11.extname(file).toLowerCase();
5184
+ const ext = path13.extname(file).toLowerCase();
4635
5185
  if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
4636
5186
  const isRootLevel = !file.includes("/");
4637
5187
  const isScriptsDir = file.startsWith("scripts/") || file.startsWith("patches/");
4638
5188
  if (!isRootLevel && !isScriptsDir) continue;
4639
- const fullPath = path11.join(projectRoot, file);
4640
- if (fs11.existsSync(fullPath)) {
4641
- const content = fs11.readFileSync(fullPath, "utf-8").toLowerCase();
5189
+ const fullPath = path13.join(projectRoot, file);
5190
+ if (fs12.existsSync(fullPath)) {
5191
+ const content = fs12.readFileSync(fullPath, "utf-8").toLowerCase();
4642
5192
  const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
4643
5193
  if (matchedPattern) {
4644
5194
  warnings.push({
@@ -4653,7 +5203,7 @@ function detectGitPatchScripts(projectRoot) {
4653
5203
  }
4654
5204
  }
4655
5205
  }
4656
- const deletedStdout = execSync5("git diff --name-only --diff-filter=D HEAD", {
5206
+ const deletedStdout = execSync7("git diff --name-only --diff-filter=D HEAD", {
4657
5207
  cwd: projectRoot,
4658
5208
  encoding: "utf-8",
4659
5209
  timeout: 3e3
@@ -4661,7 +5211,7 @@ function detectGitPatchScripts(projectRoot) {
4661
5211
  if (deletedStdout) {
4662
5212
  const deletedFiles = deletedStdout.split("\n").filter(Boolean);
4663
5213
  for (const file of deletedFiles) {
4664
- const ext = path11.extname(file).toLowerCase();
5214
+ const ext = path13.extname(file).toLowerCase();
4665
5215
  if (SCRIPT_EXTENSIONS.includes(ext)) {
4666
5216
  warnings.push({
4667
5217
  severity: "high",
@@ -4721,21 +5271,21 @@ function detectAllAntiPatterns() {
4721
5271
  }
4722
5272
 
4723
5273
  // src/engine/contextSnapshot.ts
4724
- import fs12 from "fs";
4725
- import path12 from "path";
4726
- import { execSync as execSync6 } from "child_process";
5274
+ import fs13 from "fs";
5275
+ import path14 from "path";
5276
+ import { execSync as execSync8 } from "child_process";
4727
5277
  var SNAPSHOT_DIR = "context-snapshots";
4728
5278
  function snapshotDir() {
4729
- return path12.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
5279
+ return path14.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
4730
5280
  }
4731
5281
  function ensureSnapshotDir() {
4732
5282
  const dir = snapshotDir();
4733
- if (!fs12.existsSync(dir)) {
4734
- fs12.mkdirSync(dir, { recursive: true });
5283
+ if (!fs13.existsSync(dir)) {
5284
+ fs13.mkdirSync(dir, { recursive: true });
4735
5285
  }
4736
5286
  }
4737
5287
  function snapshotFilePath(timestamp) {
4738
- return path12.join(snapshotDir(), `${timestamp}.json`);
5288
+ return path14.join(snapshotDir(), `${timestamp}.json`);
4739
5289
  }
4740
5290
  function saveSnapshot(goal) {
4741
5291
  try {
@@ -4756,7 +5306,7 @@ function saveSnapshot(goal) {
4756
5306
  hasConventions: !!summary.hasConventions
4757
5307
  };
4758
5308
  try {
4759
- fs12.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
5309
+ fs13.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
4760
5310
  } catch {
4761
5311
  }
4762
5312
  sessionMemory.recordToolCall("kuma_context", { action: "save", goal: snapshot.goal });
@@ -4764,12 +5314,12 @@ function saveSnapshot(goal) {
4764
5314
  }
4765
5315
  function listSnapshots() {
4766
5316
  const dir = snapshotDir();
4767
- if (!fs12.existsSync(dir)) return [];
5317
+ if (!fs13.existsSync(dir)) return [];
4768
5318
  try {
4769
- const files = fs12.readdirSync(dir).filter((f) => f.endsWith(".json")).sort((a, b) => b.localeCompare(a));
5319
+ const files = fs13.readdirSync(dir).filter((f) => f.endsWith(".json")).sort((a, b) => b.localeCompare(a));
4770
5320
  return files.map((f) => {
4771
5321
  try {
4772
- const content = fs12.readFileSync(path12.join(dir, f), "utf-8");
5322
+ const content = fs13.readFileSync(path14.join(dir, f), "utf-8");
4773
5323
  return JSON.parse(content);
4774
5324
  } catch {
4775
5325
  return null;
@@ -4812,7 +5362,7 @@ function formatSnapshot(snapshot) {
4812
5362
  function getGitDiffStat2() {
4813
5363
  try {
4814
5364
  const root = getProjectRoot();
4815
- const stdout = execSync6("git diff --stat", {
5365
+ const stdout = execSync8("git diff --stat", {
4816
5366
  cwd: root,
4817
5367
  encoding: "utf-8",
4818
5368
  timeout: 3e3
@@ -4957,25 +5507,25 @@ async function handleKumaContext(params) {
4957
5507
  }
4958
5508
 
4959
5509
  // src/tools/kumaInit.ts
4960
- import fs13 from "fs";
4961
- import path13 from "path";
5510
+ import fs14 from "fs";
5511
+ import path15 from "path";
4962
5512
  async function handleKumaInit(params) {
4963
5513
  const root = params.projectRoot ?? getProjectRoot();
4964
- const kumaDir = path13.join(root, ".kuma");
4965
- const memoriesDir = path13.join(kumaDir, "memories");
4966
- const initMdPath = path13.join(kumaDir, "init.md");
5514
+ const kumaDir = path15.join(root, ".kuma");
5515
+ const memoriesDir = path15.join(kumaDir, "memories");
5516
+ const initMdPath = path15.join(kumaDir, "init.md");
4967
5517
  sessionMemory.recordToolCall("kuma_init", { projectRoot: root });
4968
5518
  let rules = "";
4969
- if (fs13.existsSync(initMdPath)) {
4970
- rules = fs13.readFileSync(initMdPath, "utf-8");
5519
+ if (fs14.existsSync(initMdPath)) {
5520
+ rules = fs14.readFileSync(initMdPath, "utf-8");
4971
5521
  }
4972
5522
  const memories = [];
4973
- if (fs13.existsSync(memoriesDir)) {
5523
+ if (fs14.existsSync(memoriesDir)) {
4974
5524
  try {
4975
- const files = fs13.readdirSync(memoriesDir).filter((f) => f.endsWith(".md"));
5525
+ const files = fs14.readdirSync(memoriesDir).filter((f) => f.endsWith(".md"));
4976
5526
  for (const file of files) {
4977
5527
  try {
4978
- const content = fs13.readFileSync(path13.join(memoriesDir, file), "utf-8");
5528
+ const content = fs14.readFileSync(path15.join(memoriesDir, file), "utf-8");
4979
5529
  memories.push({ topic: file.replace(/\.md$/, ""), content });
4980
5530
  } catch {
4981
5531
  }
@@ -5019,9 +5569,9 @@ ${memoryList}`);
5019
5569
  }
5020
5570
 
5021
5571
  // src/tools/kumaRisk.ts
5022
- import path14 from "path";
5023
- import fg2 from "fast-glob";
5024
- import fs14 from "fs";
5572
+ import path16 from "path";
5573
+ import fg4 from "fast-glob";
5574
+ import fs15 from "fs";
5025
5575
  async function handleKumaRisk(params) {
5026
5576
  const { symbol, filePath, depth = 2 } = params;
5027
5577
  if (!symbol && !filePath) {
@@ -5055,7 +5605,7 @@ async function handleKumaRisk(params) {
5055
5605
  ];
5056
5606
  let entries = [];
5057
5607
  try {
5058
- entries = await fg2("**/*.{ts,tsx,js,jsx,mjs,cjs}", {
5608
+ entries = await fg4("**/*.{ts,tsx,js,jsx,mjs,cjs}", {
5059
5609
  cwd: root,
5060
5610
  ignore: ignorePatterns,
5061
5611
  onlyFiles: true,
@@ -5074,11 +5624,11 @@ async function handleKumaRisk(params) {
5074
5624
  for (const entry of entries) {
5075
5625
  if (results.length >= maxResults) break;
5076
5626
  try {
5077
- const fullPath = path14.join(root, entry);
5078
- const stat = fs14.statSync(fullPath);
5627
+ const fullPath = path16.join(root, entry);
5628
+ const stat = fs15.statSync(fullPath);
5079
5629
  if (stat.size > 5e5) continue;
5080
5630
  if (isBinaryFile(fullPath)) continue;
5081
- const content = fs14.readFileSync(fullPath, "utf-8");
5631
+ const content = fs15.readFileSync(fullPath, "utf-8");
5082
5632
  const lines = content.split("\n");
5083
5633
  for (let i = 0; i < lines.length; i++) {
5084
5634
  if (results.length >= maxResults) break;
@@ -5202,8 +5752,8 @@ function formatRiskReport(report, circularDepWarning) {
5202
5752
  }
5203
5753
 
5204
5754
  // src/tools/kumaDependencyGuard.ts
5205
- import fs15 from "fs";
5206
- import path15 from "path";
5755
+ import fs16 from "fs";
5756
+ import path17 from "path";
5207
5757
  var NATIVE_JS_ALTERNATIVES = {
5208
5758
  "lodash": [
5209
5759
  { method: "Array.prototype.map()", description: "Replace _.map()" },
@@ -5290,12 +5840,12 @@ var NATIVE_JS_ALTERNATIVES = {
5290
5840
  };
5291
5841
  function findExistingDependency(packageName) {
5292
5842
  const root = getProjectRoot();
5293
- const packageJsonPath = path15.join(root, "package.json");
5294
- if (!fs15.existsSync(packageJsonPath)) {
5843
+ const packageJsonPath = path17.join(root, "package.json");
5844
+ if (!fs16.existsSync(packageJsonPath)) {
5295
5845
  return { found: false };
5296
5846
  }
5297
5847
  try {
5298
- const pkg = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
5848
+ const pkg = JSON.parse(fs16.readFileSync(packageJsonPath, "utf-8"));
5299
5849
  const deps = pkg.dependencies || {};
5300
5850
  if (deps[packageName]) {
5301
5851
  return { found: true, version: deps[packageName] };
@@ -5311,12 +5861,12 @@ function findExistingDependency(packageName) {
5311
5861
  }
5312
5862
  function findSimilarExisting(packageName) {
5313
5863
  const root = getProjectRoot();
5314
- const packageJsonPath = path15.join(root, "package.json");
5315
- if (!fs15.existsSync(packageJsonPath)) {
5864
+ const packageJsonPath = path17.join(root, "package.json");
5865
+ if (!fs16.existsSync(packageJsonPath)) {
5316
5866
  return [];
5317
5867
  }
5318
5868
  try {
5319
- const pkg = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
5869
+ const pkg = JSON.parse(fs16.readFileSync(packageJsonPath, "utf-8"));
5320
5870
  const allDeps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
5321
5871
  const installedPackages = Object.keys(allDeps);
5322
5872
  const alternativeMap = {
@@ -5427,8 +5977,8 @@ function getEstimatedPackageSize(packageName) {
5427
5977
  }
5428
5978
 
5429
5979
  // src/tools/kumaPolicy.ts
5430
- import fs16 from "fs";
5431
- import path16 from "path";
5980
+ import fs17 from "fs";
5981
+ import path18 from "path";
5432
5982
  var DEFAULT_POLICY = {
5433
5983
  never_touch: [
5434
5984
  ".env",
@@ -5454,15 +6004,15 @@ var DEFAULT_POLICY = {
5454
6004
  };
5455
6005
  function loadPolicy() {
5456
6006
  const root = getProjectRoot();
5457
- const ymlPath = path16.join(root, ".kuma", "policy.yml");
5458
- const yamlPath = path16.join(root, ".kuma", "policy.yaml");
6007
+ const ymlPath = path18.join(root, ".kuma", "policy.yml");
6008
+ const yamlPath = path18.join(root, ".kuma", "policy.yaml");
5459
6009
  let policyContent = null;
5460
6010
  let policyPath = null;
5461
- if (fs16.existsSync(ymlPath)) {
5462
- policyContent = fs16.readFileSync(ymlPath, "utf-8");
6011
+ if (fs17.existsSync(ymlPath)) {
6012
+ policyContent = fs17.readFileSync(ymlPath, "utf-8");
5463
6013
  policyPath = ymlPath;
5464
- } else if (fs16.existsSync(yamlPath)) {
5465
- policyContent = fs16.readFileSync(yamlPath, "utf-8");
6014
+ } else if (fs17.existsSync(yamlPath)) {
6015
+ policyContent = fs17.readFileSync(yamlPath, "utf-8");
5466
6016
  policyPath = yamlPath;
5467
6017
  }
5468
6018
  if (!policyContent) {
@@ -5579,9 +6129,9 @@ function checkFilePathPolicy(filePath, policy) {
5579
6129
  if (policy.max_file_size && policy.max_file_size > 0) {
5580
6130
  try {
5581
6131
  const root = getProjectRoot();
5582
- const fullPath = path16.join(root, filePath);
5583
- if (fs16.existsSync(fullPath)) {
5584
- const sizeKB = fs16.statSync(fullPath).size / 1024;
6132
+ const fullPath = path18.join(root, filePath);
6133
+ if (fs17.existsSync(fullPath)) {
6134
+ const sizeKB = fs17.statSync(fullPath).size / 1024;
5585
6135
  if (sizeKB > policy.max_file_size) {
5586
6136
  violations.push({
5587
6137
  rule: "max_file_size",
@@ -5677,8 +6227,8 @@ function formatPolicyResult(type, value, result, policy) {
5677
6227
  }
5678
6228
 
5679
6229
  // src/engine/safetyScore.ts
5680
- import fs17 from "fs";
5681
- import path17 from "path";
6230
+ import fs18 from "fs";
6231
+ import path19 from "path";
5682
6232
  function computeSafetyScore(inputGoal) {
5683
6233
  const stats = getSessionStats(inputGoal);
5684
6234
  const checks = [];
@@ -5722,8 +6272,8 @@ function computeSafetyScore(inputGoal) {
5722
6272
  totalScore += 20;
5723
6273
  }
5724
6274
  const backupDir = getKumaBackupsDir();
5725
- if (fs17.existsSync(backupDir)) {
5726
- const backupCount = fs17.readdirSync(backupDir).filter((d) => /^\d+$/.test(d)).length;
6275
+ if (fs18.existsSync(backupDir)) {
6276
+ const backupCount = fs18.readdirSync(backupDir).filter((d) => /^\d+$/.test(d)).length;
5727
6277
  checks.push({
5728
6278
  label: "Backup Available",
5729
6279
  status: "pass",
@@ -5741,8 +6291,8 @@ function computeSafetyScore(inputGoal) {
5741
6291
  totalScore += 5;
5742
6292
  }
5743
6293
  try {
5744
- const lspPath = path17.join(getProjectRoot(), "node_modules", ".bin", "typescript-language-server");
5745
- if (fs17.existsSync(lspPath)) {
6294
+ const lspPath = path19.join(getProjectRoot(), "node_modules", ".bin", "typescript-language-server");
6295
+ if (fs18.existsSync(lspPath)) {
5746
6296
  checks.push({
5747
6297
  label: "LSP Available",
5748
6298
  status: "pass",
@@ -5964,8 +6514,8 @@ function formatSafetyScore(report) {
5964
6514
 
5965
6515
  // src/engine/kumaDb.ts
5966
6516
  import initSqlJs from "sql.js";
5967
- import fs18 from "fs";
5968
- import path18 from "path";
6517
+ import fs19 from "fs";
6518
+ import path20 from "path";
5969
6519
  var DB_FILENAME = "kuma.db";
5970
6520
  var dbInstance = null;
5971
6521
  var initPromise = null;
@@ -5978,13 +6528,13 @@ async function getDb() {
5978
6528
  async function initDb() {
5979
6529
  const SQL = await initSqlJs();
5980
6530
  const kumaDir = getKumaDir();
5981
- const dbPath = path18.join(kumaDir, DB_FILENAME);
5982
- if (!fs18.existsSync(kumaDir)) {
5983
- fs18.mkdirSync(kumaDir, { recursive: true });
6531
+ const dbPath = path20.join(kumaDir, DB_FILENAME);
6532
+ if (!fs19.existsSync(kumaDir)) {
6533
+ fs19.mkdirSync(kumaDir, { recursive: true });
5984
6534
  }
5985
6535
  let db;
5986
- if (fs18.existsSync(dbPath)) {
5987
- const buffer = fs18.readFileSync(dbPath);
6536
+ if (fs19.existsSync(dbPath)) {
6537
+ const buffer = fs19.readFileSync(dbPath);
5988
6538
  db = new SQL.Database(buffer);
5989
6539
  } else {
5990
6540
  db = new SQL.Database();
@@ -6001,10 +6551,10 @@ function saveDb(db) {
6001
6551
  if (!d) return;
6002
6552
  try {
6003
6553
  const kumaDir = getKumaDir();
6004
- const dbPath = path18.join(kumaDir, DB_FILENAME);
6554
+ const dbPath = path20.join(kumaDir, DB_FILENAME);
6005
6555
  const data = d.export();
6006
6556
  const buffer = Buffer.from(data);
6007
- fs18.writeFileSync(dbPath, buffer);
6557
+ fs19.writeFileSync(dbPath, buffer);
6008
6558
  } catch (err) {
6009
6559
  console.error(`[KumaDB] Failed to save database: ${err}`);
6010
6560
  }
@@ -6111,26 +6661,26 @@ function createSchema(db) {
6111
6661
  }
6112
6662
 
6113
6663
  // src/engine/kumaSelfHeal.ts
6114
- import { execSync as execSync7 } from "child_process";
6115
- import fs19 from "fs";
6116
- import path19 from "path";
6664
+ import { execSync as execSync9 } from "child_process";
6665
+ import fs20 from "fs";
6666
+ import path21 from "path";
6117
6667
  import crypto from "crypto";
6118
6668
  function contentFingerprint(filePath) {
6119
6669
  try {
6120
- const fullPath = path19.join(getProjectRoot(), filePath);
6121
- if (!fs19.existsSync(fullPath)) return null;
6122
- const stat = fs19.statSync(fullPath);
6670
+ const fullPath = path21.join(getProjectRoot(), filePath);
6671
+ if (!fs20.existsSync(fullPath)) return null;
6672
+ const stat = fs20.statSync(fullPath);
6123
6673
  if (!stat.isFile()) return null;
6124
- const fd = fs19.openSync(fullPath, "r");
6674
+ const fd = fs20.openSync(fullPath, "r");
6125
6675
  try {
6126
6676
  const size = stat.size;
6127
6677
  const readSize = Math.min(1024, size);
6128
6678
  const head = Buffer.alloc(readSize);
6129
- fs19.readSync(fd, head, 0, readSize, 0);
6679
+ fs20.readSync(fd, head, 0, readSize, 0);
6130
6680
  let tail = Buffer.alloc(0);
6131
6681
  if (size > 2048) {
6132
6682
  tail = Buffer.alloc(1024);
6133
- fs19.readSync(fd, tail, 0, 1024, size - 1024);
6683
+ fs20.readSync(fd, tail, 0, 1024, size - 1024);
6134
6684
  }
6135
6685
  const hash = crypto.createHash("md5");
6136
6686
  hash.update(head);
@@ -6138,7 +6688,7 @@ function contentFingerprint(filePath) {
6138
6688
  hash.update(String(size));
6139
6689
  return hash.digest("hex");
6140
6690
  } finally {
6141
- fs19.closeSync(fd);
6691
+ fs20.closeSync(fd);
6142
6692
  }
6143
6693
  } catch {
6144
6694
  return null;
@@ -6147,8 +6697,8 @@ function contentFingerprint(filePath) {
6147
6697
  function findByFingerprint(fingerprint, oldName) {
6148
6698
  try {
6149
6699
  const root = getProjectRoot();
6150
- const ext = path19.extname(oldName);
6151
- const result = execSync7(
6700
+ const ext = path21.extname(oldName);
6701
+ const result = execSync9(
6152
6702
  `find . -name "*${ext}" -type f -not -path "./node_modules/*" -not -path "./.git/*" -not -path "./dist/*" -not -path "./build/*" 2>/dev/null | head -200`,
6153
6703
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 1e4 }
6154
6704
  ).trim();
@@ -6181,8 +6731,8 @@ async function detectStaleNodes() {
6181
6731
  const row = stmt.getAsObject();
6182
6732
  const filePath = row.file_path;
6183
6733
  if (filePath.startsWith("search::") || filePath.startsWith("api_route::")) continue;
6184
- const fullPath = path19.join(root, filePath);
6185
- if (!fs19.existsSync(fullPath)) {
6734
+ const fullPath = path21.join(root, filePath);
6735
+ if (!fs20.existsSync(fullPath)) {
6186
6736
  const newPath = findRenamedPath(filePath);
6187
6737
  if (newPath) {
6188
6738
  stale.push({
@@ -6195,7 +6745,7 @@ async function detectStaleNodes() {
6195
6745
  });
6196
6746
  } else {
6197
6747
  const oldFingerprint = contentFingerprint(filePath);
6198
- const contentMatch = oldFingerprint ? findByFingerprint(oldFingerprint, path19.basename(filePath)) : null;
6748
+ const contentMatch = oldFingerprint ? findByFingerprint(oldFingerprint, path21.basename(filePath)) : null;
6199
6749
  stale.push({
6200
6750
  nodeId: row.id,
6201
6751
  type: row.type,
@@ -6216,7 +6766,7 @@ async function detectStaleNodes() {
6216
6766
  function findRenamedPath(oldPath) {
6217
6767
  try {
6218
6768
  const root = getProjectRoot();
6219
- const output = execSync7(
6769
+ const output = execSync9(
6220
6770
  `git log --follow --diff-filter=R --name-only --format="" -1 -- "${oldPath}"`,
6221
6771
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 5e3 }
6222
6772
  ).trim();
@@ -6297,8 +6847,8 @@ async function healOnQuery(filePaths) {
6297
6847
  try {
6298
6848
  const stalePaths = filePaths.filter((fp) => {
6299
6849
  if (!fp || fp.startsWith("search::") || fp.startsWith("api_route::")) return false;
6300
- const fullPath = path19.join(getProjectRoot(), fp);
6301
- return !fs19.existsSync(fullPath);
6850
+ const fullPath = path21.join(getProjectRoot(), fp);
6851
+ return !fs20.existsSync(fullPath);
6302
6852
  });
6303
6853
  if (stalePaths.length === 0) return { healed: 0 };
6304
6854
  let healed = 0;
@@ -6827,8 +7377,8 @@ async function pruneExperiences(keepPerTool = 50) {
6827
7377
 
6828
7378
  // src/engine/lspClient.ts
6829
7379
  import { spawn as spawn2 } from "child_process";
6830
- import path20 from "path";
6831
- import fs20 from "fs";
7380
+ import path22 from "path";
7381
+ import fs21 from "fs";
6832
7382
  var LSPClient = class {
6833
7383
  process = null;
6834
7384
  requestId = 0;
@@ -6866,8 +7416,8 @@ var LSPClient = class {
6866
7416
  console.error(`[LSP] typescript-language-server not found. LSP features will fallback to regex.`);
6867
7417
  return;
6868
7418
  }
6869
- const tsconfigPath = path20.join(root, "tsconfig.json");
6870
- if (!fs20.existsSync(tsconfigPath)) {
7419
+ const tsconfigPath = path22.join(root, "tsconfig.json");
7420
+ if (!fs21.existsSync(tsconfigPath)) {
6871
7421
  console.error(`[LSP] Warning: tsconfig.json not found at "${root}". Running in implicit project mode.`);
6872
7422
  }
6873
7423
  this.process = spawn2(lspBinary, ["--stdio", "--log-level=4"], {
@@ -6929,19 +7479,19 @@ var LSPClient = class {
6929
7479
  /** Resolve the typescript-language-server binary from local or global install */
6930
7480
  resolveLspBinary(projectRoot) {
6931
7481
  const candidates = [
6932
- path20.join(projectRoot, "node_modules", ".bin", "typescript-language-server"),
6933
- path20.join(projectRoot, "..", "node_modules", ".bin", "typescript-language-server")
7482
+ path22.join(projectRoot, "node_modules", ".bin", "typescript-language-server"),
7483
+ path22.join(projectRoot, "..", "node_modules", ".bin", "typescript-language-server")
6934
7484
  ];
6935
7485
  for (const candidate of candidates) {
6936
- if (fs20.existsSync(candidate)) {
7486
+ if (fs21.existsSync(candidate)) {
6937
7487
  return candidate;
6938
7488
  }
6939
7489
  }
6940
7490
  try {
6941
7491
  const envPath = process.env.PATH ?? "";
6942
- for (const dir of envPath.split(path20.delimiter)) {
6943
- const binPath = path20.join(dir, "typescript-language-server");
6944
- if (fs20.existsSync(binPath)) {
7492
+ for (const dir of envPath.split(path22.delimiter)) {
7493
+ const binPath = path22.join(dir, "typescript-language-server");
7494
+ if (fs21.existsSync(binPath)) {
6945
7495
  return binPath;
6946
7496
  }
6947
7497
  }
@@ -6963,7 +7513,7 @@ var LSPClient = class {
6963
7513
  this.openDocuments.add(filePath);
6964
7514
  let content;
6965
7515
  try {
6966
- content = fs20.readFileSync(filePath, "utf-8");
7516
+ content = fs21.readFileSync(filePath, "utf-8");
6967
7517
  } catch {
6968
7518
  content = "";
6969
7519
  }
@@ -7180,8 +7730,8 @@ var LSPClient = class {
7180
7730
  var lspClient = new LSPClient();
7181
7731
 
7182
7732
  // src/tools/lspTools.ts
7183
- import fs21 from "fs";
7184
- import path21 from "path";
7733
+ import fs22 from "fs";
7734
+ import path23 from "path";
7185
7735
  async function handleFindReferences(params) {
7186
7736
  const { filePath, line, character } = params;
7187
7737
  const validation = validateFilePath(filePath);
@@ -7189,7 +7739,7 @@ async function handleFindReferences(params) {
7189
7739
  return `Error: ${validation.error.message}`;
7190
7740
  }
7191
7741
  const resolvedPath = validation.resolvedPath;
7192
- if (!fs21.existsSync(resolvedPath)) {
7742
+ if (!fs22.existsSync(resolvedPath)) {
7193
7743
  return `Error: File not found: "${filePath}".`;
7194
7744
  }
7195
7745
  if (!lspClient.isAvailable()) {
@@ -7214,7 +7764,7 @@ No references found for symbol at this position.`;
7214
7764
  const enrichedRefs = references.map((ref) => {
7215
7765
  let lineContent = "";
7216
7766
  try {
7217
- const content = fs21.readFileSync(ref.filePath, "utf-8");
7767
+ const content = fs22.readFileSync(ref.filePath, "utf-8");
7218
7768
  const lines2 = content.split("\n");
7219
7769
  lineContent = lines2[ref.line]?.trim() ?? "";
7220
7770
  } catch {
@@ -7230,11 +7780,11 @@ No references found for symbol at this position.`;
7230
7780
  const projectRoot = getProjectRoot();
7231
7781
  const lines = [
7232
7782
  `**Find References** \u2014 ${enrichedRefs.length} references found`,
7233
- `\u{1F4CD} File: ${path21.relative(projectRoot, resolvedPath)}:${line + 1}:${character + 1}`,
7783
+ `\u{1F4CD} File: ${path23.relative(projectRoot, resolvedPath)}:${line + 1}:${character + 1}`,
7234
7784
  ""
7235
7785
  ];
7236
7786
  for (const [file, refs] of grouped) {
7237
- const relPath = path21.relative(projectRoot, file);
7787
+ const relPath = path23.relative(projectRoot, file);
7238
7788
  lines.push(`**\u{1F4C4} ${relPath}:**`);
7239
7789
  for (const ref of refs) {
7240
7790
  const loc = `L${ref.line + 1}:${ref.character + 1}`;
@@ -7255,7 +7805,7 @@ async function handleGoToDefinition(params) {
7255
7805
  return `Error: ${validation.error.message}`;
7256
7806
  }
7257
7807
  const resolvedPath = validation.resolvedPath;
7258
- if (!fs21.existsSync(resolvedPath)) {
7808
+ if (!fs22.existsSync(resolvedPath)) {
7259
7809
  return `Error: File not found: "${filePath}".`;
7260
7810
  }
7261
7811
  if (!lspClient.isAvailable()) {
@@ -7278,10 +7828,10 @@ Try selecting a smaller/cleaner position, or install typescript-language-server
7278
7828
  Cannot find definition for symbol at this position.`;
7279
7829
  }
7280
7830
  const projectRoot = getProjectRoot();
7281
- const relPath = path21.relative(projectRoot, definition.filePath);
7831
+ const relPath = path23.relative(projectRoot, definition.filePath);
7282
7832
  let lineContent = "";
7283
7833
  try {
7284
- const content = fs21.readFileSync(definition.filePath, "utf-8");
7834
+ const content = fs22.readFileSync(definition.filePath, "utf-8");
7285
7835
  const lines2 = content.split("\n");
7286
7836
  lineContent = lines2[definition.line]?.trim() ?? "";
7287
7837
  } catch {
@@ -7313,7 +7863,7 @@ async function handleRenameSymbol(params) {
7313
7863
  return `Error: ${validation.error.message}`;
7314
7864
  }
7315
7865
  const resolvedPath = validation.resolvedPath;
7316
- if (!fs21.existsSync(resolvedPath)) {
7866
+ if (!fs22.existsSync(resolvedPath)) {
7317
7867
  return `Error: File not found: "${filePath}".`;
7318
7868
  }
7319
7869
  if (!lspClient.isAvailable()) {
@@ -7344,7 +7894,7 @@ Make sure:
7344
7894
  const fileChanges = [];
7345
7895
  for (const change of result.changes) {
7346
7896
  try {
7347
- const content = fs21.readFileSync(change.filePath, "utf-8");
7897
+ const content = fs22.readFileSync(change.filePath, "utf-8");
7348
7898
  const lines2 = content.split("\n");
7349
7899
  const sortedEdits = [...change.edits].sort((a, b) => {
7350
7900
  if (b.line !== a.line) return b.line - a.line;
@@ -7358,10 +7908,10 @@ Make sure:
7358
7908
  lines2[edit.line] = before + edit.newText + after;
7359
7909
  }
7360
7910
  }
7361
- fs21.writeFileSync(change.filePath, lines2.join("\n"), "utf-8");
7911
+ fs22.writeFileSync(change.filePath, lines2.join("\n"), "utf-8");
7362
7912
  totalEdits += change.edits.length;
7363
7913
  fileChanges.push({
7364
- filePath: path21.relative(projectRoot, change.filePath),
7914
+ filePath: path23.relative(projectRoot, change.filePath),
7365
7915
  editCount: change.edits.length
7366
7916
  });
7367
7917
  } catch (err) {
@@ -7388,14 +7938,14 @@ async function handleGetTypeInfo(params) {
7388
7938
  return `Error: ${validation.error.message}`;
7389
7939
  }
7390
7940
  const resolvedPath = validation.resolvedPath;
7391
- if (!fs21.existsSync(resolvedPath)) {
7941
+ if (!fs22.existsSync(resolvedPath)) {
7392
7942
  return `Error: File not found: "${filePath}".`;
7393
7943
  }
7394
7944
  if (!lspClient.isAvailable()) {
7395
7945
  sessionMemory.recordToolCall("lsp_query", { action: "type", filePath, line, character, fallback: "line-context" });
7396
7946
  console.error(`[LSP] typescript-language-server not found. Using file-read fallback for type info.`);
7397
7947
  try {
7398
- const fileContent = fs21.readFileSync(resolvedPath, "utf-8");
7948
+ const fileContent = fs22.readFileSync(resolvedPath, "utf-8");
7399
7949
  const fileLines = fileContent.split("\n");
7400
7950
  const targetLine = fileLines[line];
7401
7951
  if (!targetLine) {
@@ -7403,7 +7953,7 @@ async function handleGetTypeInfo(params) {
7403
7953
  }
7404
7954
  const symbolName = extractSymbolAtPosition(resolvedPath, line, character);
7405
7955
  const projectRoot = getProjectRoot();
7406
- const relPath = path21.relative(projectRoot, resolvedPath);
7956
+ const relPath = path23.relative(projectRoot, resolvedPath);
7407
7957
  const contextStart = Math.max(0, line - 3);
7408
7958
  const contextEnd = Math.min(fileLines.length, line + 4);
7409
7959
  const contextLines = [];
@@ -7438,7 +7988,7 @@ async function handleGetTypeInfo(params) {
7438
7988
  No type info for this position.`;
7439
7989
  }
7440
7990
  const projectRoot = getProjectRoot();
7441
- const relPath = path21.relative(projectRoot, resolvedPath);
7991
+ const relPath = path23.relative(projectRoot, resolvedPath);
7442
7992
  const lines = [
7443
7993
  `\u{1F4CB} **Type Info** \u2014 \`${relPath}:${line + 1}:${character + 1}\``,
7444
7994
  "",
@@ -7479,7 +8029,7 @@ async function handleLspQuery(params) {
7479
8029
  }
7480
8030
  function extractSymbolAtPosition(filePath, line, character) {
7481
8031
  try {
7482
- const content = fs21.readFileSync(filePath, "utf-8");
8032
+ const content = fs22.readFileSync(filePath, "utf-8");
7483
8033
  const lines = content.split("\n");
7484
8034
  const targetLine = lines[line];
7485
8035
  if (!targetLine) return null;
@@ -7497,9 +8047,9 @@ function extractSymbolAtPosition(filePath, line, character) {
7497
8047
  }
7498
8048
  async function fallbackGrepReferences(symbolName, _filePath, _line, _character) {
7499
8049
  try {
7500
- const { default: fg4 } = await import("fast-glob");
8050
+ const { default: fg6 } = await import("fast-glob");
7501
8051
  const root = getProjectRoot();
7502
- const tsFiles = await fg4(["**/*.{ts,tsx,js,jsx}"], {
8052
+ const tsFiles = await fg6(["**/*.{ts,tsx,js,jsx}"], {
7503
8053
  cwd: root,
7504
8054
  ignore: ["node_modules/**", "dist/**", ".git/**"],
7505
8055
  onlyFiles: true,
@@ -7510,7 +8060,7 @@ async function fallbackGrepReferences(symbolName, _filePath, _line, _character)
7510
8060
  const regex = new RegExp(`\\b${escapedSymbol}\\b`, "g");
7511
8061
  for (const file of tsFiles.slice(0, 100)) {
7512
8062
  try {
7513
- const content = fs21.readFileSync(file, "utf-8");
8063
+ const content = fs22.readFileSync(file, "utf-8");
7514
8064
  const lines2 = content.split("\n");
7515
8065
  for (let i = 0; i < lines2.length; i++) {
7516
8066
  if (regex.test(lines2[i])) {
@@ -7539,7 +8089,7 @@ No references found. Symbol may not be used in other files.`;
7539
8089
  ""
7540
8090
  ];
7541
8091
  if (grouped.size > 1) {
7542
- const fileList = [...grouped.keys()].map((f) => path21.relative(projectRoot, f)).join(", ");
8092
+ const fileList = [...grouped.keys()].map((f) => path23.relative(projectRoot, f)).join(", ");
7543
8093
  lines.push(`**Ambiguity note:** Found matches across ${grouped.size} different files (${fileList}).`);
7544
8094
  lines.push(` Verify each match is the right scope \u2014 results may include same-named symbols.`);
7545
8095
  lines.push("");
@@ -7556,7 +8106,7 @@ No references found. Symbol may not be used in other files.`;
7556
8106
  }
7557
8107
  }
7558
8108
  for (const [file, refs] of grouped) {
7559
- const relPath = path21.relative(projectRoot, file);
8109
+ const relPath = path23.relative(projectRoot, file);
7560
8110
  lines.push(`**\u{1F4C4} ${relPath}:**`);
7561
8111
  for (const ref of refs) {
7562
8112
  lines.push(` \u2514 L${ref.line} \u2014 ${ref.content}`);
@@ -7571,9 +8121,9 @@ No references found. Symbol may not be used in other files.`;
7571
8121
  }
7572
8122
  async function fallbackGrepDefinition(symbolName) {
7573
8123
  try {
7574
- const { default: fg4 } = await import("fast-glob");
8124
+ const { default: fg6 } = await import("fast-glob");
7575
8125
  const root = getProjectRoot();
7576
- const tsFiles = await fg4(["**/*.{ts,tsx,js,jsx}"], {
8126
+ const tsFiles = await fg6(["**/*.{ts,tsx,js,jsx}"], {
7577
8127
  cwd: root,
7578
8128
  ignore: ["node_modules/**", "dist/**", ".git/**"],
7579
8129
  onlyFiles: true,
@@ -7590,14 +8140,14 @@ async function fallbackGrepDefinition(symbolName) {
7590
8140
  ];
7591
8141
  for (const file of tsFiles) {
7592
8142
  try {
7593
- const content = fs21.readFileSync(file, "utf-8");
8143
+ const content = fs22.readFileSync(file, "utf-8");
7594
8144
  const lines = content.split("\n");
7595
8145
  for (let i = 0; i < lines.length; i++) {
7596
8146
  const trimmed = lines[i].trim();
7597
8147
  for (const pattern of declPatterns) {
7598
8148
  if (pattern.test(trimmed)) {
7599
8149
  const projectRoot = getProjectRoot();
7600
- const relPath = path21.relative(projectRoot, file);
8150
+ const relPath = path23.relative(projectRoot, file);
7601
8151
  return [
7602
8152
  `\u{1F4CD} **Go to Definition**`,
7603
8153
  `\u{1F4C4} File: \`${relPath}\``,
@@ -7620,11 +8170,11 @@ Cannot find definition.`;
7620
8170
  }
7621
8171
 
7622
8172
  // src/engine/kumaTimeMachine.ts
7623
- import { execSync as execSync8 } from "child_process";
8173
+ import { execSync as execSync10 } from "child_process";
7624
8174
  function getBlameForFile(filePath) {
7625
8175
  const root = getProjectRoot();
7626
8176
  try {
7627
- const output = execSync8(
8177
+ const output = execSync10(
7628
8178
  `git blame --line-porcelain -- "${filePath}"`,
7629
8179
  { cwd: root, encoding: "utf-8", maxBuffer: 2 * 1024 * 1024, timeout: 1e4 }
7630
8180
  );
@@ -7670,7 +8220,7 @@ function getBlameForFile(filePath) {
7670
8220
  function getFileHistory(filePath, maxCount = 20) {
7671
8221
  const root = getProjectRoot();
7672
8222
  try {
7673
- const output = execSync8(
8223
+ const output = execSync10(
7674
8224
  `git log --follow --format="%H||%an||%ai||%s" --shortstat -- "${filePath}"`,
7675
8225
  { cwd: root, encoding: "utf-8", maxBuffer: 2 * 1024 * 1024, timeout: 1e4 }
7676
8226
  );
@@ -7705,7 +8255,7 @@ async function getSymbolTimeline(symbolName, filePath, symbolType = "function")
7705
8255
  const blame = getBlameForFile(filePath);
7706
8256
  const pattern = getSymbolPattern(symbolName, symbolType);
7707
8257
  const root = getProjectRoot();
7708
- const content = execSync8(`git show HEAD:"${filePath}"`, {
8258
+ const content = execSync10(`git show HEAD:"${filePath}"`, {
7709
8259
  cwd: root,
7710
8260
  encoding: "utf-8",
7711
8261
  maxBuffer: 2 * 1024 * 1024,
@@ -7732,7 +8282,7 @@ async function getSymbolTimeline(symbolName, filePath, symbolType = "function")
7732
8282
  const entries = [];
7733
8283
  for (const [hash, data] of commitMap) {
7734
8284
  try {
7735
- const msg = execSync8(
8285
+ const msg = execSync10(
7736
8286
  `git log -1 --format="%s" ${hash}`,
7737
8287
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 3e3 }
7738
8288
  ).trim();
@@ -7917,10 +8467,10 @@ async function getIntentPatterns(limit = 10) {
7917
8467
  ORDER BY id ASC
7918
8468
  `);
7919
8469
  pathStmt.bind([session.id]);
7920
- const path29 = [];
8470
+ const path31 = [];
7921
8471
  while (pathStmt.step()) {
7922
8472
  const row = pathStmt.getAsObject();
7923
- path29.push(row.tool_name);
8473
+ path31.push(row.tool_name);
7924
8474
  }
7925
8475
  pathStmt.free();
7926
8476
  const existing = clusterMap.get(clusterKey) || {
@@ -7929,7 +8479,7 @@ async function getIntentPatterns(limit = 10) {
7929
8479
  totalDuration: 0,
7930
8480
  lastUsed: 0
7931
8481
  };
7932
- existing.paths.push(path29);
8482
+ existing.paths.push(path31);
7933
8483
  existing.successes.push(session.success);
7934
8484
  existing.lastUsed = Math.max(existing.lastUsed, session.startedAt);
7935
8485
  clusterMap.set(clusterKey, existing);
@@ -8037,9 +8587,9 @@ function formatIntentSuggestion(intent, suggestion) {
8037
8587
  }
8038
8588
 
8039
8589
  // src/engine/kumaArchGuard.ts
8040
- import fg3 from "fast-glob";
8041
- import fs22 from "fs";
8042
- import path22 from "path";
8590
+ import fg5 from "fast-glob";
8591
+ import fs23 from "fs";
8592
+ import path24 from "path";
8043
8593
  var ARCHITECTURES = [
8044
8594
  {
8045
8595
  name: "clean-architecture",
@@ -8130,8 +8680,8 @@ var ARCHITECTURES = [
8130
8680
  ];
8131
8681
  function detectArchitecture() {
8132
8682
  const root = getProjectRoot();
8133
- const srcDir = path22.join(root, "src");
8134
- if (!fs22.existsSync(srcDir)) {
8683
+ const srcDir = path24.join(root, "src");
8684
+ if (!fs23.existsSync(srcDir)) {
8135
8685
  return getArchitectureProfile("flat");
8136
8686
  }
8137
8687
  const scores = ARCHITECTURES.map((arch) => {
@@ -8142,7 +8692,7 @@ function detectArchitecture() {
8142
8692
  totalPatterns++;
8143
8693
  const globPattern = pattern.replace(/\/\*\*$/, "/**");
8144
8694
  try {
8145
- const matches = fg3.sync(globPattern, { cwd: root, onlyFiles: false, deep: 1 });
8695
+ const matches = fg5.sync(globPattern, { cwd: root, onlyFiles: false, deep: 1 });
8146
8696
  if (matches.length > 0) matchCount++;
8147
8697
  } catch {
8148
8698
  }
@@ -8153,10 +8703,10 @@ function detectArchitecture() {
8153
8703
  scores.sort((a, b) => b.score - a.score);
8154
8704
  const best = scores[0];
8155
8705
  if (!best || best.score < 0.2) {
8156
- if (fs22.existsSync(path22.join(srcDir, "domain")) || fs22.existsSync(path22.join(srcDir, "entities"))) {
8706
+ if (fs23.existsSync(path24.join(srcDir, "domain")) || fs23.existsSync(path24.join(srcDir, "entities"))) {
8157
8707
  return getArchitectureProfile("clean-architecture");
8158
8708
  }
8159
- if (fs22.existsSync(path22.join(srcDir, "data")) || fs22.existsSync(path22.join(srcDir, "business"))) {
8709
+ if (fs23.existsSync(path24.join(srcDir, "data")) || fs23.existsSync(path24.join(srcDir, "business"))) {
8160
8710
  return getArchitectureProfile("layered-architecture");
8161
8711
  }
8162
8712
  return getArchitectureProfile("flat");
@@ -8227,7 +8777,7 @@ async function scanFilesystemForViolations(profile) {
8227
8777
  const arch = profile || detectArchitecture();
8228
8778
  const root = getProjectRoot();
8229
8779
  try {
8230
- const files = await fg3(["src/**/*.{ts,tsx,js,jsx}"], {
8780
+ const files = await fg5(["src/**/*.{ts,tsx,js,jsx}"], {
8231
8781
  cwd: root,
8232
8782
  ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/*.d.ts"],
8233
8783
  onlyFiles: true
@@ -8236,7 +8786,7 @@ async function scanFilesystemForViolations(profile) {
8236
8786
  const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
8237
8787
  for (const file of files.slice(0, 200)) {
8238
8788
  try {
8239
- const content = fs22.readFileSync(path22.join(root, file), "utf-8");
8789
+ const content = fs23.readFileSync(path24.join(root, file), "utf-8");
8240
8790
  const imports = [];
8241
8791
  let match;
8242
8792
  while ((match = importRegex.exec(content)) !== null) {
@@ -8249,16 +8799,16 @@ async function scanFilesystemForViolations(profile) {
8249
8799
  if (!imp.startsWith(".") && !imp.startsWith("/") && !imp.startsWith("src/")) continue;
8250
8800
  let targetFile = imp;
8251
8801
  if (imp.startsWith(".")) {
8252
- const resolved = path22.resolve(path22.dirname(file), imp);
8253
- targetFile = path22.relative(root, resolved);
8802
+ const resolved = path24.resolve(path24.dirname(file), imp);
8803
+ targetFile = path24.relative(root, resolved);
8254
8804
  } else if (imp.startsWith("/")) {
8255
8805
  targetFile = imp.substring(1);
8256
8806
  }
8257
- if (!path22.extname(targetFile)) {
8807
+ if (!path24.extname(targetFile)) {
8258
8808
  const extVariants = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
8259
8809
  let found = false;
8260
8810
  for (const ext of extVariants) {
8261
- if (fs22.existsSync(path22.join(root, targetFile + ext))) {
8811
+ if (fs23.existsSync(path24.join(root, targetFile + ext))) {
8262
8812
  targetFile += ext;
8263
8813
  found = true;
8264
8814
  break;
@@ -8342,20 +8892,20 @@ function formatArchitectureDetection(profile) {
8342
8892
  }
8343
8893
 
8344
8894
  // src/engine/kumaMemory.ts
8345
- import fs23 from "fs";
8346
- import path23 from "path";
8895
+ import fs24 from "fs";
8896
+ import path25 from "path";
8347
8897
  var MEMORY_DIR = ".kuma/memories";
8348
8898
  function scoreMemoryRelevance(context, limit = 5) {
8349
8899
  const results = [];
8350
- const kumaDir = path23.join(getProjectRoot(), MEMORY_DIR);
8351
- if (!fs23.existsSync(kumaDir)) return [];
8900
+ const kumaDir = path25.join(getProjectRoot(), MEMORY_DIR);
8901
+ if (!fs24.existsSync(kumaDir)) return [];
8352
8902
  const terms = context.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
8353
8903
  if (terms.length === 0) return [];
8354
8904
  try {
8355
- const files = fs23.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
8905
+ const files = fs24.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
8356
8906
  for (const file of files) {
8357
8907
  try {
8358
- const content = fs23.readFileSync(path23.join(kumaDir, file), "utf-8");
8908
+ const content = fs24.readFileSync(path25.join(kumaDir, file), "utf-8");
8359
8909
  const lower = content.toLowerCase();
8360
8910
  let matchCount = 0;
8361
8911
  for (const term of terms) {
@@ -8435,7 +8985,7 @@ function formatDecisionTemplate() {
8435
8985
  }
8436
8986
 
8437
8987
  // src/engine/kumaContextEngine.ts
8438
- import { execSync as execSync9 } from "child_process";
8988
+ import { execSync as execSync11 } from "child_process";
8439
8989
  var STOP_WORDS = /* @__PURE__ */ new Set([
8440
8990
  "the",
8441
8991
  "this",
@@ -8552,7 +9102,7 @@ async function searchNodesFts(db, terms) {
8552
9102
  function getFileRecencySeconds(filePath) {
8553
9103
  try {
8554
9104
  const escapedPath = filePath.replace(/"/g, '\\"');
8555
- const result = execSync9(
9105
+ const result = execSync11(
8556
9106
  'git log -1 --format=%ct -- "' + escapedPath + '" 2>/dev/null || echo 0',
8557
9107
  { encoding: "utf-8", timeout: 2e3 }
8558
9108
  );
@@ -9269,9 +9819,9 @@ async function buildOwnershipMap(db, focus, limit) {
9269
9819
  const dirGroups = /* @__PURE__ */ new Map();
9270
9820
  while (stmt.step()) {
9271
9821
  const row = stmt.getAsObject();
9272
- const path29 = row.file_path || row.name;
9273
- const dir = topDir(path29);
9274
- if (focus && !path29.includes(focus)) continue;
9822
+ const path31 = row.file_path || row.name;
9823
+ const dir = topDir(path31);
9824
+ if (focus && !path31.includes(focus)) continue;
9275
9825
  if (!dirGroups.has(dir)) dirGroups.set(dir, []);
9276
9826
  const existing = dirGroups.get(dir);
9277
9827
  const emoji = row.type === "function" ? "\u{1F527}" : row.type === "file" ? "\u{1F4C4}" : "\u{1F4CC}";
@@ -9312,8 +9862,8 @@ function mermaidId(name) {
9312
9862
  }
9313
9863
 
9314
9864
  // src/engine/kumaHeatMap.ts
9315
- import fs24 from "fs";
9316
- import path24 from "path";
9865
+ import fs25 from "fs";
9866
+ import path26 from "path";
9317
9867
  async function computeHeatMap() {
9318
9868
  const history = sessionMemory.getToolCallHistory(100);
9319
9869
  const db = await getDb();
@@ -9322,7 +9872,7 @@ async function computeHeatMap() {
9322
9872
  const p = call.params;
9323
9873
  const fp = p.filePath || p.file || "";
9324
9874
  if (!fp) continue;
9325
- const dir = path24.dirname(fp).split("/")[0] || ".";
9875
+ const dir = path26.dirname(fp).split("/")[0] || ".";
9326
9876
  const entry = dirData.get(dir) || { edits: 0, reads: 0, failures: 0 };
9327
9877
  if (call.toolName === "precise_diff_editor" || call.toolName === "batch_file_writer") {
9328
9878
  entry.edits++;
@@ -9350,12 +9900,12 @@ async function computeHeatMap() {
9350
9900
  } catch {
9351
9901
  }
9352
9902
  const root = getProjectRoot();
9353
- const srcDir = path24.join(root, "src");
9354
- if (fs24.existsSync(srcDir)) {
9903
+ const srcDir = path26.join(root, "src");
9904
+ if (fs25.existsSync(srcDir)) {
9355
9905
  try {
9356
- for (const entry of fs24.readdirSync(srcDir, { withFileTypes: true })) {
9906
+ for (const entry of fs25.readdirSync(srcDir, { withFileTypes: true })) {
9357
9907
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
9358
- const count = countFiles(path24.join(srcDir, entry.name));
9908
+ const count = countFiles(path26.join(srcDir, entry.name));
9359
9909
  if (!dirFileCounts.has(entry.name) || dirFileCounts.get(entry.name) < count) {
9360
9910
  dirFileCounts.set(entry.name, count);
9361
9911
  }
@@ -9465,9 +10015,9 @@ function formatHeatMap(report) {
9465
10015
  function countFiles(dir) {
9466
10016
  let count = 0;
9467
10017
  try {
9468
- for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
10018
+ for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
9469
10019
  if (entry.name.startsWith(".")) continue;
9470
- const full = path24.join(dir, entry.name);
10020
+ const full = path26.join(dir, entry.name);
9471
10021
  if (entry.isDirectory()) {
9472
10022
  count += countFiles(full);
9473
10023
  } else if (/\.(ts|tsx|js|jsx|go|rs|py|java|kt)$/.test(entry.name)) {
@@ -9767,72 +10317,72 @@ function simpleHash(str) {
9767
10317
  }
9768
10318
 
9769
10319
  // src/engine/kumaLock.ts
9770
- import fs25 from "fs";
9771
- import path25 from "path";
10320
+ import fs26 from "fs";
10321
+ import path27 from "path";
9772
10322
  var LOCKS_DIR = ".kuma/locks";
9773
10323
  function locksDir() {
9774
- return path25.join(getProjectRoot(), LOCKS_DIR);
10324
+ return path27.join(getProjectRoot(), LOCKS_DIR);
9775
10325
  }
9776
10326
  function ensureLocksDir() {
9777
10327
  const dir = locksDir();
9778
- if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
10328
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
9779
10329
  }
9780
10330
  function lockPath(filePath) {
9781
10331
  const safeName = filePath.replace(/[^a-zA-Z0-9_./-]/g, "_");
9782
- return path25.join(locksDir(), `${safeName}.lock.json`);
10332
+ return path27.join(locksDir(), `${safeName}.lock.json`);
9783
10333
  }
9784
10334
  function acquireLock(filePath, agentId) {
9785
10335
  ensureLocksDir();
9786
10336
  const lp = lockPath(filePath);
9787
10337
  const id = agentId || `agent-${process.pid}`;
9788
- if (fs25.existsSync(lp)) {
10338
+ if (fs26.existsSync(lp)) {
9789
10339
  try {
9790
- const existing = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10340
+ const existing = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9791
10341
  if (existing.agentId === id) {
9792
10342
  return `\u{1F512} **Already locked** by you (${id}) on ${new Date(existing.acquiredAt).toISOString()}`;
9793
10343
  }
9794
10344
  const elapsed = Math.floor((Date.now() - existing.acquiredAt) / 1e3);
9795
10345
  if (elapsed > 300) {
9796
- fs25.unlinkSync(lp);
10346
+ fs26.unlinkSync(lp);
9797
10347
  } else {
9798
10348
  return `\u{1F512} **Locked** by ${existing.agentId} since ${new Date(existing.acquiredAt).toISOString()} (${elapsed}s ago)`;
9799
10349
  }
9800
10350
  } catch {
9801
- fs25.unlinkSync(lp);
10351
+ fs26.unlinkSync(lp);
9802
10352
  }
9803
10353
  }
9804
10354
  const entry = { filePath, agentId: id, acquiredAt: Date.now(), status: "locked" };
9805
- fs25.writeFileSync(lp, JSON.stringify(entry, null, 2), "utf-8");
10355
+ fs26.writeFileSync(lp, JSON.stringify(entry, null, 2), "utf-8");
9806
10356
  return `\u{1F513} **Lock acquired** on \`${filePath}\` by ${id}`;
9807
10357
  }
9808
10358
  function releaseLock(filePath, agentId) {
9809
10359
  ensureLocksDir();
9810
10360
  const lp = lockPath(filePath);
9811
10361
  const id = agentId || `agent-${process.pid}`;
9812
- if (!fs25.existsSync(lp)) {
10362
+ if (!fs26.existsSync(lp)) {
9813
10363
  return `\u26A0\uFE0F No lock found for \`${filePath}\``;
9814
10364
  }
9815
10365
  try {
9816
- const existing = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10366
+ const existing = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9817
10367
  if (existing.agentId !== id) {
9818
10368
  return `\u26A0\uFE0F Cannot release lock held by ${existing.agentId}. Use force:true to override.`;
9819
10369
  }
9820
- fs25.unlinkSync(lp);
10370
+ fs26.unlinkSync(lp);
9821
10371
  return `\u{1F513} **Lock released** on \`${filePath}\``;
9822
10372
  } catch {
9823
- fs25.unlinkSync(lp);
10373
+ fs26.unlinkSync(lp);
9824
10374
  return `\u{1F513} **Lock released** (force cleanup) on \`${filePath}\``;
9825
10375
  }
9826
10376
  }
9827
10377
  function listLocks() {
9828
10378
  ensureLocksDir();
9829
10379
  const dir = locksDir();
9830
- const files = fs25.readdirSync(dir).filter((f) => f.endsWith(".lock.json"));
10380
+ const files = fs26.readdirSync(dir).filter((f) => f.endsWith(".lock.json"));
9831
10381
  if (files.length === 0) return "\u{1F513} No active locks.";
9832
10382
  const lines = ["\u{1F512} **Active Locks:**", ""];
9833
10383
  for (const f of files) {
9834
10384
  try {
9835
- const entry = JSON.parse(fs25.readFileSync(path25.join(dir, f), "utf-8"));
10385
+ const entry = JSON.parse(fs26.readFileSync(path27.join(dir, f), "utf-8"));
9836
10386
  const elapsed = Math.floor((Date.now() - entry.acquiredAt) / 1e3);
9837
10387
  lines.push(` \u2022 \`${entry.filePath}\` \u2014 locked by ${entry.agentId} (${elapsed}s ago)`);
9838
10388
  } catch {
@@ -9843,9 +10393,9 @@ function listLocks() {
9843
10393
  }
9844
10394
  function isLocked(filePath) {
9845
10395
  const lp = lockPath(filePath);
9846
- if (!fs25.existsSync(lp)) return { locked: false };
10396
+ if (!fs26.existsSync(lp)) return { locked: false };
9847
10397
  try {
9848
- const entry = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10398
+ const entry = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9849
10399
  return { locked: true, by: entry.agentId, since: entry.acquiredAt };
9850
10400
  } catch {
9851
10401
  return { locked: false };
@@ -9855,16 +10405,16 @@ function cleanStaleLocks() {
9855
10405
  ensureLocksDir();
9856
10406
  const dir = locksDir();
9857
10407
  let count = 0;
9858
- for (const f of fs25.readdirSync(dir).filter((f2) => f2.endsWith(".lock.json"))) {
10408
+ for (const f of fs26.readdirSync(dir).filter((f2) => f2.endsWith(".lock.json"))) {
9859
10409
  try {
9860
- const entry = JSON.parse(fs25.readFileSync(path25.join(dir, f), "utf-8"));
10410
+ const entry = JSON.parse(fs26.readFileSync(path27.join(dir, f), "utf-8"));
9861
10411
  if (Date.now() - entry.acquiredAt > 3e5) {
9862
- fs25.unlinkSync(path25.join(dir, f));
10412
+ fs26.unlinkSync(path27.join(dir, f));
9863
10413
  count++;
9864
10414
  }
9865
10415
  } catch {
9866
10416
  try {
9867
- fs25.unlinkSync(path25.join(dir, f));
10417
+ fs26.unlinkSync(path27.join(dir, f));
9868
10418
  count++;
9869
10419
  } catch {
9870
10420
  }
@@ -10212,13 +10762,13 @@ function formatConfidence(report, target) {
10212
10762
  }
10213
10763
 
10214
10764
  // src/engine/kumaDNA.ts
10215
- import path26 from "path";
10765
+ import path28 from "path";
10216
10766
  async function generateDNA() {
10217
10767
  try {
10218
10768
  const db = await getDb();
10219
10769
  const summary = sessionMemory.getSummary();
10220
10770
  const root = getProjectRoot();
10221
- const projectName = path26.basename(root);
10771
+ const projectName = path28.basename(root);
10222
10772
  let totalFiles = 0, totalFunctions = 0, totalTests = 0;
10223
10773
  try {
10224
10774
  const filesR = db.exec("SELECT COUNT(*) as c FROM nodes WHERE type = 'file'");
@@ -11067,14 +11617,14 @@ async function handleSafetyCheck(params) {
11067
11617
  }
11068
11618
 
11069
11619
  // src/engine/kumaCollective.ts
11070
- import fs26 from "fs";
11071
- import path27 from "path";
11620
+ import fs27 from "fs";
11621
+ import path29 from "path";
11072
11622
  var DEFAULT_ENDPOINT = process.env.KUMA_COLLECTIVE_URL || "";
11073
11623
  function getSyncConfig() {
11074
11624
  try {
11075
- const configPath = path27.join(getProjectRoot(), ".kuma", "config.json");
11076
- if (fs26.existsSync(configPath)) {
11077
- const raw = fs26.readFileSync(configPath, "utf-8");
11625
+ const configPath = path29.join(getProjectRoot(), ".kuma", "config.json");
11626
+ if (fs27.existsSync(configPath)) {
11627
+ const raw = fs27.readFileSync(configPath, "utf-8");
11078
11628
  const config = JSON.parse(raw);
11079
11629
  if (config.collective) {
11080
11630
  return {
@@ -11094,12 +11644,12 @@ function getSyncConfig() {
11094
11644
  }
11095
11645
  function getInstanceId() {
11096
11646
  try {
11097
- const idPath = path27.join(getProjectRoot(), ".kuma", ".instance-id");
11098
- if (fs26.existsSync(idPath)) {
11099
- return fs26.readFileSync(idPath, "utf-8").trim();
11647
+ const idPath = path29.join(getProjectRoot(), ".kuma", ".instance-id");
11648
+ if (fs27.existsSync(idPath)) {
11649
+ return fs27.readFileSync(idPath, "utf-8").trim();
11100
11650
  }
11101
11651
  const id = `anon-${Math.random().toString(36).slice(2, 10)}`;
11102
- fs26.writeFileSync(idPath, id, "utf-8");
11652
+ fs27.writeFileSync(idPath, id, "utf-8");
11103
11653
  return id;
11104
11654
  } catch {
11105
11655
  return "anon-unknown";
@@ -11206,13 +11756,13 @@ function exportAnonymizedPatterns() {
11206
11756
  function detectProjectLanguage() {
11207
11757
  try {
11208
11758
  const root = getProjectRoot();
11209
- if (fs26.existsSync(path27.join(root, "go.mod"))) return "go";
11210
- if (fs26.existsSync(path27.join(root, "Cargo.toml"))) return "rust";
11211
- if (fs26.existsSync(path27.join(root, "composer.json"))) return "php";
11212
- if (fs26.existsSync(path27.join(root, "pyproject.toml")) || fs26.existsSync(path27.join(root, "requirements.txt"))) return "python";
11213
- if (fs26.existsSync(path27.join(root, "Gemfile"))) return "ruby";
11214
- if (fs26.existsSync(path27.join(root, "pom.xml")) || fs26.existsSync(path27.join(root, "build.gradle"))) return "java";
11215
- if (fs26.existsSync(path27.join(root, "package.json"))) return "typescript";
11759
+ if (fs27.existsSync(path29.join(root, "go.mod"))) return "go";
11760
+ if (fs27.existsSync(path29.join(root, "Cargo.toml"))) return "rust";
11761
+ if (fs27.existsSync(path29.join(root, "composer.json"))) return "php";
11762
+ if (fs27.existsSync(path29.join(root, "pyproject.toml")) || fs27.existsSync(path29.join(root, "requirements.txt"))) return "python";
11763
+ if (fs27.existsSync(path29.join(root, "Gemfile"))) return "ruby";
11764
+ if (fs27.existsSync(path29.join(root, "pom.xml")) || fs27.existsSync(path29.join(root, "build.gradle"))) return "java";
11765
+ if (fs27.existsSync(path29.join(root, "package.json"))) return "typescript";
11216
11766
  return "unknown";
11217
11767
  } catch {
11218
11768
  return "unknown";
@@ -11368,9 +11918,9 @@ function formatCollectivePatterns(patterns) {
11368
11918
  }
11369
11919
 
11370
11920
  // src/engine/kumaMarketplace.ts
11371
- import { execSync as execSync10 } from "child_process";
11372
- import fs27 from "fs";
11373
- import path28 from "path";
11921
+ import { execSync as execSync12 } from "child_process";
11922
+ import fs28 from "fs";
11923
+ import path30 from "path";
11374
11924
  var BUILT_IN_TEMPLATES = [
11375
11925
  // ── Framework Web (JS/TS) ──
11376
11926
  {
@@ -11585,7 +12135,7 @@ async function listMarketplace() {
11585
12135
  `\u{1F4E6} ${templates.length} template(s) available`,
11586
12136
  ""
11587
12137
  ];
11588
- const LANG_MAP = [
12138
+ const LANG_MAP2 = [
11589
12139
  { lang: "typescript", keywords: ["typescript", "hono", "elysia", "tanstack", "zustand", "prisma", "drizzle", "shadcn"] },
11590
12140
  { lang: "javascript", keywords: ["javascript", "express"] },
11591
12141
  { lang: "go", keywords: ["go", "gin"] },
@@ -11605,7 +12155,7 @@ async function listMarketplace() {
11605
12155
  general: "\u{1F4E6}"
11606
12156
  };
11607
12157
  function detectLang(t) {
11608
- for (const entry of LANG_MAP) {
12158
+ for (const entry of LANG_MAP2) {
11609
12159
  if (entry.keywords.some((k) => t.tags.some((tag) => tag.includes(k)))) {
11610
12160
  return entry.lang;
11611
12161
  }
@@ -11650,17 +12200,17 @@ function scanNpmTemplates() {
11650
12200
  const found = [];
11651
12201
  try {
11652
12202
  const root = getProjectRoot();
11653
- const nodeModulesPath = path28.join(root, "node_modules", "@kuma-templates");
11654
- if (!fs27.existsSync(nodeModulesPath)) return found;
11655
- const packages = fs27.readdirSync(nodeModulesPath);
12203
+ const nodeModulesPath = path30.join(root, "node_modules", "@kuma-templates");
12204
+ if (!fs28.existsSync(nodeModulesPath)) return found;
12205
+ const packages = fs28.readdirSync(nodeModulesPath);
11656
12206
  for (const pkg of packages) {
11657
- const pkgPath = path28.join(nodeModulesPath, pkg, "package.json");
11658
- if (!fs27.existsSync(pkgPath)) continue;
11659
- const pkgJson = JSON.parse(fs27.readFileSync(pkgPath, "utf-8"));
11660
- const templatePath = path28.join(nodeModulesPath, pkg, "template.json");
12207
+ const pkgPath = path30.join(nodeModulesPath, pkg, "package.json");
12208
+ if (!fs28.existsSync(pkgPath)) continue;
12209
+ const pkgJson = JSON.parse(fs28.readFileSync(pkgPath, "utf-8"));
12210
+ const templatePath = path30.join(nodeModulesPath, pkg, "template.json");
11661
12211
  let nodeCount = 0, edgeCount = 0;
11662
- if (fs27.existsSync(templatePath)) {
11663
- const tmpl = JSON.parse(fs27.readFileSync(templatePath, "utf-8"));
12212
+ if (fs28.existsSync(templatePath)) {
12213
+ const tmpl = JSON.parse(fs28.readFileSync(templatePath, "utf-8"));
11664
12214
  nodeCount = tmpl.nodes?.length || 0;
11665
12215
  edgeCount = tmpl.edges?.length || 0;
11666
12216
  }
@@ -11745,8 +12295,8 @@ async function tryInstallFromNpm(templateId) {
11745
12295
  const name = templateId.replace(/^graph:/, "");
11746
12296
  try {
11747
12297
  const root = getProjectRoot();
11748
- const pkgPath = path28.join(root, "node_modules", "@kuma-templates", name, "template.json");
11749
- if (fs27.existsSync(pkgPath)) {
12298
+ const pkgPath = path30.join(root, "node_modules", "@kuma-templates", name, "template.json");
12299
+ if (fs28.existsSync(pkgPath)) {
11750
12300
  return await loadNpmTemplate(pkgPath, name);
11751
12301
  }
11752
12302
  } catch {
@@ -11754,14 +12304,14 @@ async function tryInstallFromNpm(templateId) {
11754
12304
  try {
11755
12305
  const npmPackage = `@kuma-templates/${name}`;
11756
12306
  const root = getProjectRoot();
11757
- execSync10(`npm install --no-save ${npmPackage}`, {
12307
+ execSync12(`npm install --no-save ${npmPackage}`, {
11758
12308
  cwd: root,
11759
12309
  encoding: "utf-8",
11760
12310
  timeout: 3e4,
11761
12311
  stdio: "pipe"
11762
12312
  });
11763
- const templatePath = path28.join(root, "node_modules", "@kuma-templates", name, "template.json");
11764
- if (fs27.existsSync(templatePath)) {
12313
+ const templatePath = path30.join(root, "node_modules", "@kuma-templates", name, "template.json");
12314
+ if (fs28.existsSync(templatePath)) {
11765
12315
  return await loadNpmTemplate(templatePath, name);
11766
12316
  }
11767
12317
  return [
@@ -11779,7 +12329,7 @@ async function tryInstallFromNpm(templateId) {
11779
12329
  }
11780
12330
  }
11781
12331
  async function loadNpmTemplate(templatePath, name) {
11782
- const raw = fs27.readFileSync(templatePath, "utf-8");
12332
+ const raw = fs28.readFileSync(templatePath, "utf-8");
11783
12333
  const template = JSON.parse(raw);
11784
12334
  const db = await getDb();
11785
12335
  let nodeCount = 0;
@@ -12166,8 +12716,12 @@ async function handleCore(action, params) {
12166
12716
  return await handleBatchFileWriter(params);
12167
12717
  case "lsp":
12168
12718
  return await handleLspQuery({ ...params, action: params.lspAction });
12719
+ case "find":
12720
+ return await handleKumaFind(params);
12721
+ case "stats":
12722
+ return await handleKumaStats(params);
12169
12723
  default:
12170
- return `\u26A0\uFE0F Unknown action "${action}" for kuma_core. Use: grep, read, edit, batch, lsp`;
12724
+ return `\u26A0\uFE0F Unknown action "${action}" for kuma_core. Use: grep, read, edit, batch, lsp, find, stats`;
12171
12725
  }
12172
12726
  }
12173
12727
  async function handleVerify(action, params) {
@@ -12551,9 +13105,9 @@ function registerAllTools(server) {
12551
13105
  });
12552
13106
  server.tool(
12553
13107
  "kuma_core",
12554
- "Core coding tools: grep (search), read (file), edit (search-and-replace), batch (create files), lsp (rename/reference).",
13108
+ "Core coding tools: grep (search code), read (open file), edit (search-and-replace), batch (create files), lsp (rename/reference), find (files by name/pattern), stats (file/dir/project statistics).",
12555
13109
  {
12556
- action: z.enum(["grep", "read", "edit", "batch", "lsp"]).describe("Action: grep=search code, read=open file, edit=edit with safety, batch=create files, lsp=semantic analysis"),
13110
+ action: z.enum(["grep", "read", "edit", "batch", "lsp", "find", "stats"]).describe("Action: grep=search code, read=open file, edit=edit with safety, batch=create files, lsp=semantic analysis, find=files by name, stats=file/dir/project stats"),
12557
13111
  // grep params
12558
13112
  query: z.string().optional().describe("Regex pattern for grep action"),
12559
13113
  queries: z.array(z.string()).optional().describe("Multiple regex patterns (OR'd together, single pass)"),
@@ -12571,13 +13125,14 @@ function registerAllTools(server) {
12571
13125
  endLine: z.number().min(1).optional().describe("End line (1-indexed) for read"),
12572
13126
  chunkStrategy: z.enum(["full", "smart", "outline"]).optional().default("smart").describe("Read strategy"),
12573
13127
  readOutputMode: z.enum(["rich", "raw"]).optional().default("rich").describe("Output format for read: rich=line numbers, raw=content only"),
12574
- // edit params
13128
+ // edit params — BATCH SUPPORTED: pass multiple edits[] in 1 call (faster than calling edit 5x)
12575
13129
  edits: z.array(z.object({
12576
13130
  searchBlock: z.string().min(1).describe("Code to replace"),
12577
13131
  replaceBlock: z.string().describe("Replacement code"),
12578
13132
  allowMultiple: z.boolean().optional().default(false).describe("Allow multiple replacements"),
12579
13133
  fuzzyThreshold: z.number().min(0).max(1).optional().default(0.85).describe("Fuzzy threshold")
12580
- })).min(1).max(10).optional().describe("Array of edits (for edit action)"),
13134
+ })).min(1).max(10).optional().describe("Array of edits (for edit action) \u2014 batch multiple edits in 1 call for speed"),
13135
+ safe: z.boolean().optional().default(true).describe("Safe mode: true=create backup + safety checks (default), false=skip backup for speed (40x faster, no rollback)"),
12581
13136
  dryRun: z.boolean().optional().default(false).describe("Preview without writing"),
12582
13137
  version: z.union([z.number().min(1), z.literal("list")]).optional().describe("Backup version for rollback"),
12583
13138
  scope: z.enum(["file", "dir", "edit-id", "commit"]).optional().describe("Rollback scope"),
@@ -12588,6 +13143,12 @@ function registerAllTools(server) {
12588
13143
  content: z.string().describe("File content"),
12589
13144
  instructions: z.string().min(1).describe("Reason for creating")
12590
13145
  })).min(1).max(15).optional().describe("Array of files (for batch action)"),
13146
+ // find params
13147
+ name: z.string().optional().describe("File name glob pattern for find action (e.g. '*seed*', '*.tsx')"),
13148
+ path: z.string().optional().describe("Path glob pattern for find action (e.g. '**/utils/**', '*/seed*')"),
13149
+ type: z.enum(["file", "dir", "both"]).optional().default("file").describe("Type filter for find: file=files only, dir=dirs only"),
13150
+ // stats params
13151
+ target: z.enum(["file", "dir", "project"]).optional().default("project").describe("Stats target: file=single file, dir=directory, project=entire project"),
12591
13152
  // lsp params
12592
13153
  line: z.number().min(0).optional().describe("Line number (0-indexed) for LSP"),
12593
13154
  character: z.number().min(0).optional().describe("Character position (0-indexed) for LSP"),
@@ -12923,23 +13484,23 @@ async function main() {
12923
13484
  });
12924
13485
  const output = formatInitResults(results);
12925
13486
  console.log(output);
12926
- const fs28 = await import("fs");
12927
- const path29 = await import("path");
12928
- const matchaSkills = path29.resolve(process.cwd(), "skills/matcha/SKILL.md");
12929
- const matchaAgents = path29.resolve(
13487
+ const fs29 = await import("fs");
13488
+ const path31 = await import("path");
13489
+ const matchaSkills = path31.resolve(process.cwd(), "skills/matcha/SKILL.md");
13490
+ const matchaAgents = path31.resolve(
12930
13491
  process.cwd(),
12931
13492
  ".agents/skills/matcha/SKILL.md"
12932
13493
  );
12933
- const matchaRootSkills = path29.resolve(
13494
+ const matchaRootSkills = path31.resolve(
12934
13495
  process.cwd(),
12935
13496
  "skills/matcha/SKILL.md"
12936
13497
  );
12937
- const matchaAgentsMd = path29.resolve(process.cwd(), "AGENTS.md");
12938
- const matchaWindsurfRules = path29.resolve(
13498
+ const matchaAgentsMd = path31.resolve(process.cwd(), "AGENTS.md");
13499
+ const matchaWindsurfRules = path31.resolve(
12939
13500
  process.cwd(),
12940
13501
  ".windsurfrules"
12941
13502
  );
12942
- if (fs28.existsSync(matchaSkills) || fs28.existsSync(matchaAgents) || fs28.existsSync(matchaRootSkills) || fs28.existsSync(matchaAgentsMd) || fs28.existsSync(matchaWindsurfRules)) {
13503
+ if (fs29.existsSync(matchaSkills) || fs29.existsSync(matchaAgents) || fs29.existsSync(matchaRootSkills) || fs29.existsSync(matchaAgentsMd) || fs29.existsSync(matchaWindsurfRules)) {
12943
13504
  console.error(
12944
13505
  "\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!"
12945
13506
  );
@@ -12953,13 +13514,13 @@ async function main() {
12953
13514
  (async () => {
12954
13515
  try {
12955
13516
  const { generateInitMdContent } = await import("./init-INY6XOQT.js");
12956
- const fs28 = await import("fs");
12957
- const path29 = await import("path");
12958
- const initMdPath = path29.resolve(process.cwd(), ".kuma/init.md");
12959
- if (!fs28.existsSync(initMdPath)) {
12960
- const kumaDir = path29.dirname(initMdPath);
12961
- if (!fs28.existsSync(kumaDir)) fs28.mkdirSync(kumaDir, { recursive: true });
12962
- fs28.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
13517
+ const fs29 = await import("fs");
13518
+ const path31 = await import("path");
13519
+ const initMdPath = path31.resolve(process.cwd(), ".kuma/init.md");
13520
+ if (!fs29.existsSync(initMdPath)) {
13521
+ const kumaDir = path31.dirname(initMdPath);
13522
+ if (!fs29.existsSync(kumaDir)) fs29.mkdirSync(kumaDir, { recursive: true });
13523
+ fs29.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
12963
13524
  console.error(`[${SERVER_NAME}] Auto-generated .kuma/init.md`);
12964
13525
  }
12965
13526
  } catch (err) {
@@ -12970,8 +13531,8 @@ async function main() {
12970
13531
  try {
12971
13532
  const { detectAgent, getSkillPath, getAgentLabel } = await import("./agentDetector-HROEABDO.js");
12972
13533
  const { generateSkill, getSecondaryFiles } = await import("./skillGenerator-F6YO4H5D.js");
12973
- const fs28 = await import("fs");
12974
- const path29 = await import("path");
13534
+ const fs29 = await import("fs");
13535
+ const path31 = await import("path");
12975
13536
  const detection = detectAgent();
12976
13537
  if (!detection.primary) {
12977
13538
  console.error(`[${SERVER_NAME}] No AI agent detected \u2014 skipping auto-skill creation`);
@@ -12979,22 +13540,22 @@ async function main() {
12979
13540
  }
12980
13541
  const agentType = detection.primary;
12981
13542
  const skillPath = getSkillPath(agentType);
12982
- const fullPath = path29.resolve(process.cwd(), skillPath);
12983
- if (fs28.existsSync(fullPath)) {
13543
+ const fullPath = path31.resolve(process.cwd(), skillPath);
13544
+ if (fs29.existsSync(fullPath)) {
12984
13545
  console.error(`[${SERVER_NAME}] Skill exists for ${getAgentLabel(agentType)} \u2014 skipping`);
12985
13546
  return;
12986
13547
  }
12987
- const dir = path29.dirname(fullPath);
12988
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
12989
- fs28.writeFileSync(fullPath, generateSkill(agentType), "utf-8");
13548
+ const dir = path31.dirname(fullPath);
13549
+ if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
13550
+ fs29.writeFileSync(fullPath, generateSkill(agentType), "utf-8");
12990
13551
  console.error(`[${SERVER_NAME}] Auto-created ${skillPath} for ${getAgentLabel(agentType)}`);
12991
13552
  const secondaryFiles = getSecondaryFiles(agentType);
12992
13553
  for (const sf of secondaryFiles) {
12993
- const sfPath = path29.resolve(process.cwd(), sf.path);
12994
- const sfDir = path29.dirname(sfPath);
12995
- if (!fs28.existsSync(sfPath)) {
12996
- if (!fs28.existsSync(sfDir)) fs28.mkdirSync(sfDir, { recursive: true });
12997
- fs28.writeFileSync(sfPath, sf.content, "utf-8");
13554
+ const sfPath = path31.resolve(process.cwd(), sf.path);
13555
+ const sfDir = path31.dirname(sfPath);
13556
+ if (!fs29.existsSync(sfPath)) {
13557
+ if (!fs29.existsSync(sfDir)) fs29.mkdirSync(sfDir, { recursive: true });
13558
+ fs29.writeFileSync(sfPath, sf.content, "utf-8");
12998
13559
  console.error(`[${SERVER_NAME}] Auto-created ${sf.path}`);
12999
13560
  }
13000
13561
  }