@plumpslabs/kuma 2.2.6 → 2.2.7

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 +887 -412
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -760,11 +760,48 @@ var CircuitBreakerStore = class {
760
760
  var circuitBreaker = new CircuitBreakerStore();
761
761
 
762
762
  // src/tools/preciseDiffEditor.ts
763
+ function fastApplyEdit(content, edit) {
764
+ const { searchBlock, replaceBlock, allowMultiple = false } = edit;
765
+ const count = countOccurrences(content, searchBlock);
766
+ if (count === 0) {
767
+ return {
768
+ success: false,
769
+ matched: 0,
770
+ replaced: 0,
771
+ error: `DIFF_MISMATCH: searchBlock not found (fast mode - exact match only)`,
772
+ details: content
773
+ };
774
+ }
775
+ const newContent = allowMultiple ? replaceAll(content, searchBlock, replaceBlock) : content.replace(searchBlock, replaceBlock);
776
+ return {
777
+ success: true,
778
+ matched: count,
779
+ replaced: allowMultiple ? count : 1,
780
+ details: newContent
781
+ };
782
+ }
783
+ function formatFastEditResult(results, filePath) {
784
+ const successCount = results.filter((r) => r.success).length;
785
+ const failCount = results.filter((r) => !r.success).length;
786
+ if (failCount === 0) {
787
+ return `\u26A1 Fast edit: ${filePath} \u2014 ${successCount} edits applied.`;
788
+ }
789
+ const lines = [`\u26A1 Fast edit: ${filePath}`];
790
+ for (let i = 0; i < results.length; i++) {
791
+ const r = results[i];
792
+ if (r.success) {
793
+ lines.push(` [${i + 1}] \u2705 ${r.matched}x matched, ${r.replaced}x replaced`);
794
+ } else {
795
+ lines.push(` [${i + 1}] \u274C ${r.error}`);
796
+ }
797
+ }
798
+ return lines.join("\n");
799
+ }
763
800
  function generateEditId() {
764
801
  return `edit-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
765
802
  }
766
803
  async function handlePreciseDiffEditor(params) {
767
- const { filePath, edits, dryRun = false, action, scope } = params;
804
+ const { filePath, edits, safe = true, dryRun = false, action, scope } = params;
768
805
  if (action === "rollback") {
769
806
  return handleRollbackEdit({ filePath, version: params.version, scope: scope || "file", editId: params.editId });
770
807
  }
@@ -776,11 +813,13 @@ async function handlePreciseDiffEditor(params) {
776
813
  return `Error: ${validation.error.message}`;
777
814
  }
778
815
  const resolvedPath = validation.resolvedPath;
779
- const cbResult = circuitBreaker.check("precise_diff_editor", { filePath });
780
- if (!cbResult.allowed) {
781
- return `\u26A0\uFE0F ${cbResult.reason}
816
+ if (safe) {
817
+ const cbResult = circuitBreaker.check("precise_diff_editor", { filePath });
818
+ if (!cbResult.allowed) {
819
+ return `\u26A0\uFE0F ${cbResult.reason}
782
820
 
783
821
  Try reading the file first with smart_file_picker to verify current content.`;
822
+ }
784
823
  }
785
824
  if (!fs3.existsSync(resolvedPath)) {
786
825
  return `Error: File not found: "${filePath}".
@@ -792,6 +831,22 @@ Use batch_file_writer to create a new file.`;
792
831
  const results = [];
793
832
  for (let i = 0; i < edits.length; i++) {
794
833
  const edit = edits[i];
834
+ if (!safe) {
835
+ const result2 = fastApplyEdit(currentContent, edit);
836
+ if (result2.success) {
837
+ fs3.writeFileSync(resolvedPath, result2.details, "utf-8");
838
+ currentContent = result2.details;
839
+ sessionMemory.recordToolCall("precise_diff_editor", {
840
+ filePath,
841
+ editIndex: i,
842
+ fast: true,
843
+ success: true
844
+ });
845
+ sessionMemory.addModifiedFile(filePath);
846
+ }
847
+ results.push(result2);
848
+ continue;
849
+ }
795
850
  const result = applyEdit(currentContent, edit, resolvedPath, i, dryRun);
796
851
  if (result.success) {
797
852
  if (!dryRun) {
@@ -817,6 +872,9 @@ Use batch_file_writer to create a new file.`;
817
872
  if (dryRun) {
818
873
  return formatDryRunResult(results, filePath, originalContent);
819
874
  }
875
+ if (!safe) {
876
+ return formatFastEditResult(results, filePath);
877
+ }
820
878
  return formatDiffResult(results, filePath);
821
879
  } catch (err) {
822
880
  return `Error editing file "${filePath}": ${err instanceof Error ? err.message : String(err)}`;
@@ -1196,10 +1254,10 @@ File "${filePath}" restored from backup: "${relBackupPath}".`;
1196
1254
  }
1197
1255
  async function handleCommitRollback(filePath, version) {
1198
1256
  try {
1199
- const { execSync: execSync11 } = await import("child_process");
1257
+ const { execSync: execSync13 } = await import("child_process");
1200
1258
  const root = getProjectRoot();
1201
1259
  if (version === "list") {
1202
- const log = execSync11("git log --oneline -20", { cwd: root, encoding: "utf-8" });
1260
+ const log = execSync13("git log --oneline -20", { cwd: root, encoding: "utf-8" });
1203
1261
  const lines = log.trim().split("\n").map((l) => ` ${l}`).join("\n");
1204
1262
  return [
1205
1263
  `\u{1F4CB} **Recent Commits:**`,
@@ -1209,7 +1267,7 @@ async function handleCommitRollback(filePath, version) {
1209
1267
  `\u{1F4A1} Use { action: "rollback", scope: "commit", version: <N> } to restore a specific commit.`
1210
1268
  ].join("\n");
1211
1269
  }
1212
- const status = execSync11("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
1270
+ const status = execSync13("git status --porcelain", { cwd: root, encoding: "utf-8" }).trim();
1213
1271
  if (status) {
1214
1272
  return [
1215
1273
  `\u26A0\uFE0F **Uncommitted changes detected.**`,
@@ -1230,10 +1288,10 @@ async function handleCommitRollback(filePath, version) {
1230
1288
  target = "HEAD~1";
1231
1289
  }
1232
1290
  if (filePath) {
1233
- execSync11(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
1291
+ execSync13(`git checkout ${target} -- "${filePath}"`, { cwd: root, encoding: "utf-8" });
1234
1292
  return `\u2705 **Commit Rollback** \u2014 File "${filePath}" restored from ${target}.`;
1235
1293
  } else {
1236
- execSync11(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
1294
+ execSync13(`git checkout ${target}`, { cwd: root, encoding: "utf-8" });
1237
1295
  return `\u2705 **Commit Rollback** \u2014 Project restored to ${target}.`;
1238
1296
  }
1239
1297
  } catch (err) {
@@ -1509,13 +1567,419 @@ function formatBatchResult(results, totalRequested) {
1509
1567
  return lines.join("\n");
1510
1568
  }
1511
1569
 
1512
- // src/tools/safeTerminalExec.ts
1513
- import fs6 from "fs";
1570
+ // src/tools/kumaFind.ts
1571
+ import fg2 from "fast-glob";
1572
+ import path5 from "path";
1573
+ import { execSync as execSync2 } from "child_process";
1574
+ var IGNORE_PATTERNS2 = [
1575
+ "**/node_modules/**",
1576
+ "**/.next/**",
1577
+ "**/dist/**",
1578
+ "**/build/**",
1579
+ "**/.git/**",
1580
+ "**/.cache/**",
1581
+ "**/.kuma/backups/**",
1582
+ "**/coverage/**",
1583
+ "**/.nyc_output/**"
1584
+ ];
1585
+ var CACHE_TTL_MS2 = 3e4;
1586
+ var findCache = /* @__PURE__ */ new Map();
1587
+ var rgAvailable2 = null;
1588
+ function isRipgrepAvailable2() {
1589
+ if (process.env.KUMA_DISABLE_RG === "1" || process.env.KUMA_DISABLE_RG === "true") {
1590
+ return false;
1591
+ }
1592
+ if (rgAvailable2 !== null) return rgAvailable2;
1593
+ try {
1594
+ execSync2("rg --version", { stdio: "ignore", timeout: 2e3 });
1595
+ rgAvailable2 = true;
1596
+ } catch {
1597
+ rgAvailable2 = false;
1598
+ }
1599
+ return rgAvailable2;
1600
+ }
1601
+ function quotePath2(p) {
1602
+ return `"${p.replace(/"/g, '\\"')}"`;
1603
+ }
1604
+ async function handleKumaFind(params) {
1605
+ const {
1606
+ name = "*",
1607
+ path: pathPattern,
1608
+ extensions,
1609
+ targetFolder,
1610
+ maxResults = 30,
1611
+ type = "file",
1612
+ outputMode = "rich",
1613
+ compact = false
1614
+ } = params;
1615
+ const recursiveName = !pathPattern && !targetFolder && !name.startsWith("**/") ? `**/${name}` : name;
1616
+ const cacheKey = `${recursiveName}:${pathPattern ?? ""}:${targetFolder ?? ""}:${type}:${maxResults}:${extensions ? extensions.join(",") : ""}`;
1617
+ const cached = findCache.get(cacheKey);
1618
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS2) {
1619
+ return cached.results;
1620
+ }
1621
+ const projectRoot = getProjectRoot();
1622
+ let entries;
1623
+ if (isRipgrepAvailable2() && type !== "dir") {
1624
+ entries = await tryRipgrepFind({
1625
+ name: recursiveName,
1626
+ pathPattern,
1627
+ targetFolder,
1628
+ extensions,
1629
+ maxResults,
1630
+ projectRoot
1631
+ });
1632
+ } else {
1633
+ entries = await fastGlobFind({
1634
+ name: recursiveName,
1635
+ pathPattern,
1636
+ targetFolder,
1637
+ extensions,
1638
+ maxResults,
1639
+ type,
1640
+ projectRoot
1641
+ });
1642
+ }
1643
+ if (entries.length === 0) {
1644
+ const noResult = compact ? `0:${name}` : `\u{1F50D} No files found matching "${name}"${targetFolder ? ` in ${targetFolder}` : ""}.`;
1645
+ findCache.set(cacheKey, { results: noResult, timestamp: Date.now() });
1646
+ return noResult;
1647
+ }
1648
+ sessionMemory.recordToolCall("kuma_find", {
1649
+ name,
1650
+ matchCount: entries.length,
1651
+ engine: rgAvailable2 ? "ripgrep" : "fast-glob"
1652
+ });
1653
+ let result;
1654
+ if (outputMode === "json") {
1655
+ result = JSON.stringify(entries);
1656
+ } else if (compact || outputMode === "raw") {
1657
+ result = entries.join("\n");
1658
+ } else {
1659
+ const lines = [
1660
+ `\u{1F50D} Find: "${name}"`,
1661
+ `\u{1F4C1} ${entries.length} files found${targetFolder ? ` in ${targetFolder}` : ""}`,
1662
+ "",
1663
+ ...entries.map((e, i) => `[${i + 1}] \u{1F4C4} ${e}`)
1664
+ ];
1665
+ result = lines.join("\n");
1666
+ }
1667
+ findCache.set(cacheKey, { results: result, timestamp: Date.now() });
1668
+ return result;
1669
+ }
1670
+ async function tryRipgrepFind(opts) {
1671
+ const { name, pathPattern, targetFolder, extensions, maxResults, projectRoot } = opts;
1672
+ const args = ["rg", "--files", "--no-ignore-vcs", "--color", "never"];
1673
+ for (const ignore of IGNORE_PATTERNS2) {
1674
+ const glob = ignore.replace(/^\*\*\//, "");
1675
+ args.push("-g", `!${glob}`);
1676
+ }
1677
+ args.push("-g", name);
1678
+ if (pathPattern) {
1679
+ args.push("-g", pathPattern);
1680
+ }
1681
+ if (extensions && extensions.length > 0) {
1682
+ for (const ext of extensions) {
1683
+ const clean = ext.startsWith(".") ? ext : `.${ext}`;
1684
+ args.push("-g", `*${clean}`);
1685
+ }
1686
+ }
1687
+ const searchDir = targetFolder ? path5.join(projectRoot, targetFolder) : projectRoot;
1688
+ args.push(quotePath2(searchDir));
1689
+ try {
1690
+ const output = execSync2(args.join(" "), {
1691
+ cwd: projectRoot,
1692
+ maxBuffer: 5 * 1024 * 1024,
1693
+ timeout: 1e4,
1694
+ encoding: "utf-8",
1695
+ stdio: ["ignore", "pipe", "pipe"]
1696
+ });
1697
+ if (!output || output.trim().length === 0) return [];
1698
+ let files = output.trim().split("\n").map((f) => f.trim()).filter(Boolean);
1699
+ if (targetFolder) {
1700
+ const prefix = targetFolder.replace(/\/+$/, "") + "/";
1701
+ files = files.map((f) => prefix + f);
1702
+ }
1703
+ return files.slice(0, maxResults);
1704
+ } catch (err) {
1705
+ return [];
1706
+ }
1707
+ }
1708
+ async function fastGlobFind(opts) {
1709
+ const { name, pathPattern, targetFolder, extensions, maxResults, type, projectRoot } = opts;
1710
+ const globParts = [];
1711
+ if (pathPattern) {
1712
+ globParts.push(pathPattern.replace(/^\/+/, "").replace(/\/+$/, ""));
1713
+ } else if (targetFolder) {
1714
+ globParts.push(targetFolder.replace(/^\/+/, "").replace(/\/+$/, ""));
1715
+ }
1716
+ globParts.push(name);
1717
+ const searchPattern = globParts.join("/");
1718
+ try {
1719
+ let entries = await fg2(searchPattern, {
1720
+ cwd: projectRoot,
1721
+ ignore: IGNORE_PATTERNS2,
1722
+ onlyFiles: type === "file",
1723
+ onlyDirectories: type === "dir",
1724
+ absolute: false,
1725
+ deep: 20,
1726
+ dot: false
1727
+ });
1728
+ if (extensions && extensions.length > 0 && type !== "dir") {
1729
+ const normalizedExts = extensions.map(
1730
+ (e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
1731
+ );
1732
+ entries = entries.filter((entry) => {
1733
+ const ext = path5.extname(entry).toLowerCase();
1734
+ return normalizedExts.includes(ext);
1735
+ });
1736
+ }
1737
+ return entries.slice(0, maxResults);
1738
+ } catch {
1739
+ return [];
1740
+ }
1741
+ }
1742
+
1743
+ // src/tools/kumaStats.ts
1744
+ import fs5 from "fs";
1514
1745
  import path6 from "path";
1746
+ import { execSync as execSync3 } from "child_process";
1747
+ import fg3 from "fast-glob";
1748
+ var LANG_MAP = {
1749
+ ts: "TypeScript",
1750
+ tsx: "TypeScript React",
1751
+ js: "JavaScript",
1752
+ jsx: "JavaScript React",
1753
+ py: "Python",
1754
+ go: "Go",
1755
+ rs: "Rust",
1756
+ java: "Java",
1757
+ kt: "Kotlin",
1758
+ rb: "Ruby",
1759
+ php: "PHP",
1760
+ cs: "C#",
1761
+ swift: "Swift",
1762
+ c: "C",
1763
+ cpp: "C++",
1764
+ h: "C/C++ Header",
1765
+ css: "CSS",
1766
+ scss: "SCSS",
1767
+ less: "Less",
1768
+ html: "HTML",
1769
+ json: "JSON",
1770
+ yaml: "YAML",
1771
+ yml: "YAML",
1772
+ md: "Markdown",
1773
+ sql: "SQL",
1774
+ graphql: "GraphQL",
1775
+ prisma: "Prisma",
1776
+ toml: "TOML",
1777
+ xml: "XML",
1778
+ svg: "SVG",
1779
+ sh: "Shell",
1780
+ bash: "Shell",
1781
+ dockerfile: "Dockerfile"
1782
+ };
1783
+ async function handleKumaStats(params) {
1784
+ const {
1785
+ filePath,
1786
+ target = "project",
1787
+ outputMode = "rich",
1788
+ compact = false,
1789
+ extensions
1790
+ } = params;
1791
+ if (target === "file" && filePath) {
1792
+ return await statsSingleFile(filePath, { outputMode, compact });
1793
+ }
1794
+ if (target === "dir" && filePath) {
1795
+ return await statsDirectory(filePath, { outputMode, compact, extensions });
1796
+ }
1797
+ return await statsProject({ outputMode, compact, extensions });
1798
+ }
1799
+ async function statsSingleFile(filePath, opts) {
1800
+ const { outputMode, compact } = opts;
1801
+ const validation = validateFilePath(filePath);
1802
+ if (!validation.valid) {
1803
+ return compact ? `ERR:${filePath}` : `Error: ${validation.error.message}`;
1804
+ }
1805
+ const resolvedPath = validation.resolvedPath;
1806
+ if (!fs5.existsSync(resolvedPath)) {
1807
+ return compact ? `ERR:not found` : `Error: File not found: ${filePath}`;
1808
+ }
1809
+ const stat = fs5.statSync(resolvedPath);
1810
+ const isDir = stat.isDirectory();
1811
+ if (isDir) {
1812
+ return statsDirectory(filePath, { outputMode, compact });
1813
+ }
1814
+ const content = fs5.readFileSync(resolvedPath, "utf-8");
1815
+ const lines = content.split("\n");
1816
+ const ext = path6.extname(resolvedPath).replace(".", "").toLowerCase();
1817
+ const lang = LANG_MAP[ext] || ext.toUpperCase() || "Unknown";
1818
+ const totalBytes = stat.size;
1819
+ const nonEmpty = lines.filter((l) => l.trim().length > 0).length;
1820
+ if (outputMode === "json") {
1821
+ return JSON.stringify({
1822
+ file: filePath,
1823
+ lines: lines.length,
1824
+ nonEmpty,
1825
+ size: totalBytes,
1826
+ sizeHuman: formatBytes(totalBytes),
1827
+ language: lang
1828
+ });
1829
+ }
1830
+ if (compact || outputMode === "raw") {
1831
+ return `${filePath} ${lines.length} ${formatBytes(totalBytes)} ${lang}`;
1832
+ }
1833
+ return [
1834
+ `\u{1F4CA} Stats: ${filePath}`,
1835
+ `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`,
1836
+ ` Language: ${lang}`,
1837
+ ` Lines: ${lines.length}`,
1838
+ ` Non-empty: ${nonEmpty}`,
1839
+ ` Size: ${formatBytes(totalBytes)}`
1840
+ ].join("\n");
1841
+ }
1842
+ async function statsDirectory(dirPath, opts) {
1843
+ const { outputMode, compact, extensions } = opts;
1844
+ const projectRoot = getProjectRoot();
1845
+ const resolved = path6.isAbsolute(dirPath) ? dirPath : path6.join(projectRoot, dirPath);
1846
+ if (!fs5.existsSync(resolved)) {
1847
+ return compact ? `ERR:not found ${dirPath}` : `Error: Directory not found: ${dirPath}`;
1848
+ }
1849
+ let totalFiles = 0;
1850
+ let totalLines = 0;
1851
+ let totalBytes = 0;
1852
+ const langCounts = {};
1853
+ const entries = await fg3("**/*", {
1854
+ cwd: resolved,
1855
+ onlyFiles: true,
1856
+ ignore: [
1857
+ "**/node_modules/**",
1858
+ "**/.git/**",
1859
+ "**/dist/**",
1860
+ "**/build/**",
1861
+ "**/.next/**",
1862
+ "**/coverage/**",
1863
+ "**/*.map"
1864
+ ],
1865
+ deep: 10,
1866
+ dot: false
1867
+ });
1868
+ for (const entry of entries) {
1869
+ const ext = path6.extname(entry).replace(".", "").toLowerCase();
1870
+ if (extensions && extensions.length > 0) {
1871
+ const normalizedExts = extensions.map(
1872
+ (e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`
1873
+ );
1874
+ if (!normalizedExts.includes(`.${ext}`)) continue;
1875
+ }
1876
+ totalFiles++;
1877
+ const fullPath = path6.join(resolved, entry);
1878
+ try {
1879
+ const stat = fs5.statSync(fullPath);
1880
+ if (stat.size > 1e6) {
1881
+ totalBytes += stat.size;
1882
+ continue;
1883
+ }
1884
+ const content = fs5.readFileSync(fullPath, "utf-8");
1885
+ const lineCount = content.split("\n").length;
1886
+ totalLines += lineCount;
1887
+ totalBytes += stat.size;
1888
+ const lang = LANG_MAP[ext] || ext.toUpperCase() || "Other";
1889
+ if (!langCounts[lang]) {
1890
+ langCounts[lang] = { files: 0, lines: 0 };
1891
+ }
1892
+ langCounts[lang].files++;
1893
+ langCounts[lang].lines += lineCount;
1894
+ } catch {
1895
+ try {
1896
+ const fallbackStat = fs5.statSync(fullPath);
1897
+ totalBytes += fallbackStat.size;
1898
+ } catch {
1899
+ totalBytes += 0;
1900
+ }
1901
+ }
1902
+ }
1903
+ sessionMemory.recordToolCall("kuma_stats", {
1904
+ target: dirPath,
1905
+ totalFiles,
1906
+ totalLines
1907
+ });
1908
+ if (outputMode === "json") {
1909
+ return JSON.stringify({
1910
+ directory: dirPath,
1911
+ totalFiles,
1912
+ totalLines,
1913
+ totalBytes,
1914
+ sizeHuman: formatBytes(totalBytes),
1915
+ languages: langCounts
1916
+ });
1917
+ }
1918
+ if (compact || outputMode === "raw") {
1919
+ const langs = Object.entries(langCounts).sort((a, b) => b[1].lines - a[1].lines).map(([name, counts]) => `${name} ${counts.files} ${counts.lines}`).join("\n");
1920
+ return `${dirPath} ${totalFiles} ${totalLines} ${formatBytes(totalBytes)}
1921
+ ${langs}`;
1922
+ }
1923
+ const langSummary = Object.entries(langCounts).sort((a, b) => b[1].lines - a[1].lines).slice(0, 10).map(
1924
+ ([name, counts]) => ` ${name.padEnd(20)} ${String(counts.files).padStart(5)} files ${String(counts.lines).padStart(8)} lines`
1925
+ ).join("\n");
1926
+ return [
1927
+ `\u{1F4CA} Directory Stats: ${dirPath}`,
1928
+ `\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`,
1929
+ ` Total files: ${totalFiles}`,
1930
+ ` Total lines: ${totalLines}`,
1931
+ ` Total size: ${formatBytes(totalBytes)}`,
1932
+ ``,
1933
+ `Top Languages:`,
1934
+ langSummary
1935
+ ].join("\n");
1936
+ }
1937
+ async function statsProject(opts) {
1938
+ const projectRoot = getProjectRoot();
1939
+ const projectName = path6.basename(projectRoot);
1940
+ const projectStats = await statsDirectory(projectRoot, opts);
1941
+ let branch = "unknown";
1942
+ let lastCommit = "unknown";
1943
+ try {
1944
+ branch = execSync3("git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown", {
1945
+ cwd: projectRoot,
1946
+ encoding: "utf-8",
1947
+ timeout: 2e3
1948
+ }).trim();
1949
+ lastCommit = execSync3('git log -1 --format="%h %s" 2>/dev/null || echo unknown', {
1950
+ cwd: projectRoot,
1951
+ encoding: "utf-8",
1952
+ timeout: 2e3
1953
+ }).trim();
1954
+ } catch {
1955
+ }
1956
+ if (opts.outputMode === "raw" || opts.compact) {
1957
+ return `Project: ${projectName} Branch: ${branch} ${lastCommit}
1958
+ ${projectStats}`;
1959
+ }
1960
+ return [
1961
+ `\u{1F4CA} Project: ${projectName}`,
1962
+ `\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`,
1963
+ ` Branch: ${branch}`,
1964
+ ` Last commit: ${lastCommit}`,
1965
+ ``,
1966
+ projectStats
1967
+ ].join("\n");
1968
+ }
1969
+ function formatBytes(bytes) {
1970
+ if (bytes === 0) return "0 B";
1971
+ const units = ["B", "KB", "MB", "GB"];
1972
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
1973
+ return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${units[i]}`;
1974
+ }
1975
+
1976
+ // src/tools/safeTerminalExec.ts
1977
+ import fs7 from "fs";
1978
+ import path8 from "path";
1515
1979
 
1516
1980
  // src/utils/conventionsDetector.ts
1517
- import fs5 from "fs";
1518
- import path5 from "path";
1981
+ import fs6 from "fs";
1982
+ import path7 from "path";
1519
1983
  var cachedConventions = null;
1520
1984
  async function detectConventions(forceRescan = false) {
1521
1985
  if (cachedConventions && !forceRescan) {
@@ -1546,7 +2010,7 @@ async function detectConventions(forceRescan = false) {
1546
2010
  return conventions;
1547
2011
  }
1548
2012
  function detectFramework(root) {
1549
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2013
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1550
2014
  if (pkg) {
1551
2015
  const pkgDeps = pkg.dependencies ?? {};
1552
2016
  const pkgDevDeps = pkg.devDependencies ?? {};
@@ -1591,11 +2055,11 @@ function detectFramework(root) {
1591
2055
  function scanSubProjectFrameworks(root) {
1592
2056
  const frameworks = [];
1593
2057
  try {
1594
- const entries = fs5.readdirSync(root, { withFileTypes: true });
2058
+ const entries = fs6.readdirSync(root, { withFileTypes: true });
1595
2059
  for (const entry of entries) {
1596
2060
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
1597
- const subDir = path5.join(root, entry.name);
1598
- const subPkg = readJsonSafe(path5.join(subDir, "package.json"));
2061
+ const subDir = path7.join(root, entry.name);
2062
+ const subPkg = readJsonSafe(path7.join(subDir, "package.json"));
1599
2063
  if (!subPkg) continue;
1600
2064
  const subDeps = { ...subPkg.dependencies ?? {}, ...subPkg.devDependencies ?? {} };
1601
2065
  if (subDeps.next) frameworks.push("Next.js");
@@ -1614,7 +2078,7 @@ function scanSubProjectFrameworks(root) {
1614
2078
  return frameworks;
1615
2079
  }
1616
2080
  function detectProjectType(root) {
1617
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2081
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1618
2082
  if (pkg) {
1619
2083
  const pkgDeps = pkg.dependencies ?? {};
1620
2084
  const pkgDevDeps = pkg.devDependencies ?? {};
@@ -1638,8 +2102,8 @@ function detectProjectType(root) {
1638
2102
  }
1639
2103
  function detectWorkspaces(root) {
1640
2104
  const results = [];
1641
- const pkg = readJsonSafe(path5.join(root, "package.json"));
1642
- const pnpmWorkspace = readYamlLite(path5.join(root, "pnpm-workspace.yaml"));
2105
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
2106
+ const pnpmWorkspace = readYamlLite(path7.join(root, "pnpm-workspace.yaml"));
1643
2107
  const patterns = /* @__PURE__ */ new Set();
1644
2108
  const pkgWorkspaces = pkg?.workspaces;
1645
2109
  if (Array.isArray(pkgWorkspaces)) {
@@ -1659,22 +2123,22 @@ function detectWorkspaces(root) {
1659
2123
  for (const pattern of patterns) {
1660
2124
  const match = pattern.match(/^([^*]+)\/\*$/);
1661
2125
  if (!match) continue;
1662
- const dir = path5.join(root, match[1]);
1663
- if (!fs5.existsSync(dir)) continue;
2126
+ const dir = path7.join(root, match[1]);
2127
+ if (!fs6.existsSync(dir)) continue;
1664
2128
  let entries = [];
1665
2129
  try {
1666
- entries = fs5.readdirSync(dir, { withFileTypes: true });
2130
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
1667
2131
  } catch {
1668
2132
  continue;
1669
2133
  }
1670
2134
  for (const entry of entries) {
1671
2135
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
1672
- const pkgPath = path5.join(dir, entry.name, "package.json");
2136
+ const pkgPath = path7.join(dir, entry.name, "package.json");
1673
2137
  const subPkg = readJsonSafe(pkgPath);
1674
2138
  if (!subPkg) continue;
1675
- const workspacePath = path5.join(dir, entry.name);
2139
+ const workspacePath = path7.join(dir, entry.name);
1676
2140
  results.push({
1677
- path: path5.relative(root, workspacePath),
2141
+ path: path7.relative(root, workspacePath),
1678
2142
  name: subPkg.name ?? entry.name,
1679
2143
  framework: detectFramework(workspacePath),
1680
2144
  packageManager: detectPackageManagerForDir(workspacePath)
@@ -1690,8 +2154,8 @@ function detectWorkspaces(root) {
1690
2154
  }
1691
2155
  function readYamlLite(filePath) {
1692
2156
  try {
1693
- if (!fs5.existsSync(filePath)) return null;
1694
- const text = fs5.readFileSync(filePath, "utf-8");
2157
+ if (!fs6.existsSync(filePath)) return null;
2158
+ const text = fs6.readFileSync(filePath, "utf-8");
1695
2159
  const packages = [];
1696
2160
  let inPackages = false;
1697
2161
  for (const rawLine of text.split("\n")) {
@@ -1712,7 +2176,7 @@ function readYamlLite(filePath) {
1712
2176
  }
1713
2177
  }
1714
2178
  function detectTestRunner(root) {
1715
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2179
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1716
2180
  if (!pkg) return "unknown";
1717
2181
  const pkgDeps2 = pkg.dependencies ?? {};
1718
2182
  const pkgDevDeps2 = pkg.devDependencies ?? {};
@@ -1733,7 +2197,7 @@ function detectTestRunner(root) {
1733
2197
  return "unknown";
1734
2198
  }
1735
2199
  function detectStyling(root) {
1736
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2200
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1737
2201
  if (!pkg) return "unknown";
1738
2202
  const pkgDeps3 = pkg.dependencies ?? {};
1739
2203
  const pkgDevDeps3 = pkg.devDependencies ?? {};
@@ -1746,22 +2210,22 @@ function detectStyling(root) {
1746
2210
  if (deps.less) return "Less";
1747
2211
  if (deps["@vanilla-extract/css"]) return "Vanilla Extract";
1748
2212
  if (deps.cssmodules || hasCSSModules(root)) return "CSS Modules";
1749
- const srcDir = path5.join(root, "src");
1750
- if (fs5.existsSync(srcDir)) {
2213
+ const srcDir = path7.join(root, "src");
2214
+ if (fs6.existsSync(srcDir)) {
1751
2215
  const cssFiles = findFiles(srcDir, [".css", ".scss", ".less"]);
1752
2216
  if (cssFiles.length > 0) return "Plain CSS/SCSS";
1753
2217
  }
1754
2218
  return "unknown";
1755
2219
  }
1756
2220
  function detectImportAlias(root) {
1757
- const tsconfig = readJsonSafe(path5.join(root, "tsconfig.json"));
2221
+ const tsconfig = readJsonSafe(path7.join(root, "tsconfig.json"));
1758
2222
  const tsconfigPaths = tsconfig?.compilerOptions;
1759
2223
  if (tsconfigPaths?.paths) {
1760
2224
  const paths = tsconfigPaths.paths;
1761
2225
  const alias = Object.keys(paths)[0];
1762
2226
  if (alias) return alias.replace("/*", "");
1763
2227
  }
1764
- const jsconfig = readJsonSafe(path5.join(root, "jsconfig.json"));
2228
+ const jsconfig = readJsonSafe(path7.join(root, "jsconfig.json"));
1765
2229
  const jsconfigPaths = jsconfig?.compilerOptions;
1766
2230
  if (jsconfigPaths?.paths) {
1767
2231
  const paths = jsconfigPaths.paths;
@@ -1772,15 +2236,15 @@ function detectImportAlias(root) {
1772
2236
  }
1773
2237
  function detectLintRules(root) {
1774
2238
  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");
2239
+ if (fs6.existsSync(path7.join(root, ".eslintrc"))) rules.push("ESLint (.eslintrc)");
2240
+ if (fs6.existsSync(path7.join(root, ".eslintrc.js"))) rules.push("ESLint (.eslintrc.js)");
2241
+ if (fs6.existsSync(path7.join(root, ".eslintrc.json"))) rules.push("ESLint (.eslintrc.json)");
2242
+ if (fs6.existsSync(path7.join(root, "eslint.config.js"))) rules.push("ESLint (flat config)");
2243
+ if (fs6.existsSync(path7.join(root, ".prettierrc"))) rules.push("Prettier");
2244
+ if (fs6.existsSync(path7.join(root, ".prettierrc.json"))) rules.push("Prettier");
2245
+ if (fs6.existsSync(path7.join(root, ".prettierrc.js"))) rules.push("Prettier");
2246
+ if (fs6.existsSync(path7.join(root, ".stylelintrc"))) rules.push("StyleLint");
2247
+ if (fs6.existsSync(path7.join(root, ".stylelintrc.json"))) rules.push("StyleLint");
1784
2248
  return rules;
1785
2249
  }
1786
2250
  function detectPackageManager() {
@@ -1789,26 +2253,26 @@ function detectPackageManager() {
1789
2253
  }
1790
2254
  function detectPackageManagerForDir(dir) {
1791
2255
  const root = getProjectRoot();
1792
- let currentDir = path5.resolve(dir);
2256
+ let currentDir = path7.resolve(dir);
1793
2257
  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";
2258
+ if (fs6.existsSync(path7.join(currentDir, "pnpm-lock.yaml"))) return "pnpm";
2259
+ if (fs6.existsSync(path7.join(currentDir, "yarn.lock"))) return "yarn";
2260
+ if (fs6.existsSync(path7.join(currentDir, "package-lock.json"))) return "npm";
2261
+ if (fs6.existsSync(path7.join(currentDir, "bun.lockb"))) return "bun";
1798
2262
  if (currentDir === root) break;
1799
- currentDir = path5.dirname(currentDir);
2263
+ currentDir = path7.dirname(currentDir);
1800
2264
  }
1801
2265
  return "npm";
1802
2266
  }
1803
2267
  function detectModuleSystem(root) {
1804
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2268
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1805
2269
  if (pkg?.type === "module") return "esm";
1806
- const srcDir = path5.join(root, "src");
1807
- if (fs5.existsSync(srcDir)) {
2270
+ const srcDir = path7.join(root, "src");
2271
+ if (fs6.existsSync(srcDir)) {
1808
2272
  const tsFiles = findFiles(srcDir, [".ts", ".tsx", ".js", ".jsx", ".mjs"]);
1809
2273
  for (const file of tsFiles.slice(0, 20)) {
1810
2274
  try {
1811
- const content = fs5.readFileSync(file, "utf-8");
2275
+ const content = fs6.readFileSync(file, "utf-8");
1812
2276
  if (content.includes("import ") || content.includes("export ")) return "esm";
1813
2277
  } catch {
1814
2278
  continue;
@@ -1818,22 +2282,22 @@ function detectModuleSystem(root) {
1818
2282
  return "cjs";
1819
2283
  }
1820
2284
  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";
2285
+ if (fs6.existsSync(path7.join(root, "go.mod"))) return "go";
2286
+ if (fs6.existsSync(path7.join(root, "Cargo.toml"))) return "rust";
2287
+ if (fs6.existsSync(path7.join(root, "composer.json"))) return "php";
2288
+ if (fs6.existsSync(path7.join(root, "pyproject.toml")) || fs6.existsSync(path7.join(root, "requirements.txt")) || fs6.existsSync(path7.join(root, "setup.py"))) return "python";
2289
+ if (fs6.existsSync(path7.join(root, "Gemfile"))) return "ruby";
2290
+ if (fs6.existsSync(path7.join(root, "pom.xml"))) return "java";
2291
+ if (fs6.existsSync(path7.join(root, "build.gradle")) || fs6.existsSync(path7.join(root, "build.gradle.kts"))) return "kotlin";
2292
+ if (fs6.existsSync(path7.join(root, "Package.swift"))) return "swift";
1829
2293
  try {
1830
- const entries = fs5.readdirSync(root);
2294
+ const entries = fs6.readdirSync(root);
1831
2295
  if (entries.some((e) => e.endsWith(".csproj"))) return "csharp";
1832
2296
  } catch {
1833
2297
  }
1834
- if (fs5.existsSync(path5.join(root, "tsconfig.json"))) return "typescript";
1835
- const srcDir = path5.join(root, "src");
1836
- if (fs5.existsSync(srcDir)) {
2298
+ if (fs6.existsSync(path7.join(root, "tsconfig.json"))) return "typescript";
2299
+ const srcDir = path7.join(root, "src");
2300
+ if (fs6.existsSync(srcDir)) {
1837
2301
  const tsFiles = findFiles(srcDir, [".ts", ".tsx"]);
1838
2302
  if (tsFiles.length > 0) return "typescript";
1839
2303
  const jsFiles = findFiles(srcDir, [".js", ".jsx"]);
@@ -1852,7 +2316,7 @@ function detectMultiLanguageFramework(root, language) {
1852
2316
  switch (language) {
1853
2317
  case "go": {
1854
2318
  try {
1855
- const goMod = fs5.readFileSync(path5.join(root, "go.mod"), "utf-8");
2319
+ const goMod = fs6.readFileSync(path7.join(root, "go.mod"), "utf-8");
1856
2320
  if (goMod.includes("github.com/gin-gonic/gin")) return "Gin";
1857
2321
  if (goMod.includes("github.com/labstack/echo")) return "Echo";
1858
2322
  if (goMod.includes("github.com/gofiber/fiber")) return "Fiber";
@@ -1866,7 +2330,7 @@ function detectMultiLanguageFramework(root, language) {
1866
2330
  }
1867
2331
  case "php": {
1868
2332
  try {
1869
- const composer = JSON.parse(fs5.readFileSync(path5.join(root, "composer.json"), "utf-8"));
2333
+ const composer = JSON.parse(fs6.readFileSync(path7.join(root, "composer.json"), "utf-8"));
1870
2334
  const req = composer.require ?? {};
1871
2335
  const reqDev = composer["require-dev"] ?? {};
1872
2336
  const deps = { ...req, ...reqDev };
@@ -1883,14 +2347,14 @@ function detectMultiLanguageFramework(root, language) {
1883
2347
  }
1884
2348
  case "python": {
1885
2349
  try {
1886
- if (fs5.existsSync(path5.join(root, "pyproject.toml"))) {
1887
- const content = fs5.readFileSync(path5.join(root, "pyproject.toml"), "utf-8");
2350
+ if (fs6.existsSync(path7.join(root, "pyproject.toml"))) {
2351
+ const content = fs6.readFileSync(path7.join(root, "pyproject.toml"), "utf-8");
1888
2352
  if (content.includes("django")) return "Django";
1889
2353
  if (content.includes("flask")) return "Flask";
1890
2354
  if (content.includes("fastapi")) return "FastAPI";
1891
2355
  }
1892
- if (fs5.existsSync(path5.join(root, "requirements.txt"))) {
1893
- const content = fs5.readFileSync(path5.join(root, "requirements.txt"), "utf-8");
2356
+ if (fs6.existsSync(path7.join(root, "requirements.txt"))) {
2357
+ const content = fs6.readFileSync(path7.join(root, "requirements.txt"), "utf-8");
1894
2358
  if (content.includes("django")) return "Django";
1895
2359
  if (content.includes("flask")) return "Flask";
1896
2360
  if (content.includes("fastapi")) return "FastAPI";
@@ -1903,7 +2367,7 @@ function detectMultiLanguageFramework(root, language) {
1903
2367
  }
1904
2368
  case "rust": {
1905
2369
  try {
1906
- const cargo = fs5.readFileSync(path5.join(root, "Cargo.toml"), "utf-8");
2370
+ const cargo = fs6.readFileSync(path7.join(root, "Cargo.toml"), "utf-8");
1907
2371
  if (cargo.includes("axum")) return "Axum";
1908
2372
  if (cargo.includes("actix-web")) return "Actix Web";
1909
2373
  if (cargo.includes("rocket")) return "Rocket";
@@ -1917,7 +2381,7 @@ function detectMultiLanguageFramework(root, language) {
1917
2381
  }
1918
2382
  case "java": {
1919
2383
  try {
1920
- const content = fs5.readFileSync(path5.join(root, "pom.xml"), "utf-8");
2384
+ const content = fs6.readFileSync(path7.join(root, "pom.xml"), "utf-8");
1921
2385
  if (content.includes("spring-boot")) return "Spring Boot";
1922
2386
  if (content.includes("quarkus")) return "Quarkus";
1923
2387
  if (content.includes("micronaut")) return "Micronaut";
@@ -1925,7 +2389,7 @@ function detectMultiLanguageFramework(root, language) {
1925
2389
  if (content.includes("helidon")) return "Helidon";
1926
2390
  } catch {
1927
2391
  try {
1928
- const gradle = fs5.readFileSync(path5.join(root, "build.gradle"), "utf-8");
2392
+ const gradle = fs6.readFileSync(path7.join(root, "build.gradle"), "utf-8");
1929
2393
  if (gradle.includes("spring")) return "Spring Boot";
1930
2394
  if (gradle.includes("quarkus")) return "Quarkus";
1931
2395
  } catch {
@@ -1935,7 +2399,7 @@ function detectMultiLanguageFramework(root, language) {
1935
2399
  }
1936
2400
  case "kotlin": {
1937
2401
  try {
1938
- const gradle = fs5.readFileSync(path5.join(root, "build.gradle.kts"), "utf-8");
2402
+ const gradle = fs6.readFileSync(path7.join(root, "build.gradle.kts"), "utf-8");
1939
2403
  if (gradle.includes("spring")) return "Spring Boot (Kotlin)";
1940
2404
  if (gradle.includes("ktor")) return "Ktor";
1941
2405
  } catch {
@@ -1944,7 +2408,7 @@ function detectMultiLanguageFramework(root, language) {
1944
2408
  }
1945
2409
  case "ruby": {
1946
2410
  try {
1947
- const gemfile = fs5.readFileSync(path5.join(root, "Gemfile"), "utf-8");
2411
+ const gemfile = fs6.readFileSync(path7.join(root, "Gemfile"), "utf-8");
1948
2412
  if (gemfile.includes("rails")) return "Ruby on Rails";
1949
2413
  if (gemfile.includes("sinatra")) return "Sinatra";
1950
2414
  if (gemfile.includes("hanami")) return "Hanami";
@@ -1955,10 +2419,10 @@ function detectMultiLanguageFramework(root, language) {
1955
2419
  }
1956
2420
  case "csharp": {
1957
2421
  try {
1958
- const entries = fs5.readdirSync(root);
2422
+ const entries = fs6.readdirSync(root);
1959
2423
  const csproj = entries.find((e) => e.endsWith(".csproj"));
1960
2424
  if (csproj) {
1961
- const content = fs5.readFileSync(path5.join(root, csproj), "utf-8");
2425
+ const content = fs6.readFileSync(path7.join(root, csproj), "utf-8");
1962
2426
  if (content.includes("Microsoft.AspNetCore")) return "ASP.NET Core";
1963
2427
  if (content.includes("Microsoft.Maui")) return ".NET MAUI";
1964
2428
  if (content.includes("Serilog")) return "Serilog";
@@ -1969,7 +2433,7 @@ function detectMultiLanguageFramework(root, language) {
1969
2433
  }
1970
2434
  case "swift": {
1971
2435
  try {
1972
- const content = fs5.readFileSync(path5.join(root, "Package.swift"), "utf-8");
2436
+ const content = fs6.readFileSync(path7.join(root, "Package.swift"), "utf-8");
1973
2437
  if (content.includes("vapor")) return "Vapor";
1974
2438
  if (content.includes("swift-nio")) return "SwiftNIO";
1975
2439
  if (content.includes("perfect")) return "Perfect";
@@ -1984,7 +2448,7 @@ function detectMultiLanguageFramework(root, language) {
1984
2448
  }
1985
2449
  function detectFeatures(root) {
1986
2450
  const features = [];
1987
- const pkg = readJsonSafe(path5.join(root, "package.json"));
2451
+ const pkg = readJsonSafe(path7.join(root, "package.json"));
1988
2452
  if (!pkg) return features;
1989
2453
  const pkgDeps4 = pkg.dependencies ?? {};
1990
2454
  const pkgDevDeps4 = pkg.devDependencies ?? {};
@@ -2008,25 +2472,25 @@ function detectFeatures(root) {
2008
2472
  }
2009
2473
  function readJsonSafe(filePath) {
2010
2474
  try {
2011
- if (fs5.existsSync(filePath)) {
2012
- return JSON.parse(fs5.readFileSync(filePath, "utf-8"));
2475
+ if (fs6.existsSync(filePath)) {
2476
+ return JSON.parse(fs6.readFileSync(filePath, "utf-8"));
2013
2477
  }
2014
2478
  } catch {
2015
2479
  }
2016
2480
  return null;
2017
2481
  }
2018
2482
  function hasCSSModules(root) {
2019
- const srcDir = path5.join(root, "src");
2020
- if (!fs5.existsSync(srcDir)) return false;
2483
+ const srcDir = path7.join(root, "src");
2484
+ if (!fs6.existsSync(srcDir)) return false;
2021
2485
  const files = findFiles(srcDir, [".module.css", ".module.scss", ".module.less"]);
2022
2486
  return files.length > 0;
2023
2487
  }
2024
2488
  function findFiles(dir, extensions) {
2025
2489
  const results = [];
2026
2490
  try {
2027
- const entries = fs5.readdirSync(dir, { withFileTypes: true });
2491
+ const entries = fs6.readdirSync(dir, { withFileTypes: true });
2028
2492
  for (const entry of entries) {
2029
- const fullPath = path5.join(dir, entry.name);
2493
+ const fullPath = path7.join(dir, entry.name);
2030
2494
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
2031
2495
  results.push(...findFiles(fullPath, extensions));
2032
2496
  } else if (entry.isFile()) {
@@ -2217,7 +2681,7 @@ async function handleSafeTerminalExec(params) {
2217
2681
  const workspaces = conventions?.workspaces;
2218
2682
  const matched = workspaces?.find((w) => w.name === workspace || w.path === workspace);
2219
2683
  if (matched) {
2220
- workingDir = path6.resolve(projectRoot, matched.path);
2684
+ workingDir = path8.resolve(projectRoot, matched.path);
2221
2685
  resolvedFrom = `workspace "${matched.name}" \u2192 ${matched.path}`;
2222
2686
  } else {
2223
2687
  return `\u26A0\uFE0F Workspace "${workspace}" not found. Run project_conventions first to detect workspaces, or use 'cwd' parameter with a direct path.
@@ -2225,13 +2689,13 @@ async function handleSafeTerminalExec(params) {
2225
2689
  Available workspaces: ${workspaces?.map((w) => `"${w.name}" (${w.path})`).join(", ") || "none detected"}`;
2226
2690
  }
2227
2691
  } else if (inputCwd) {
2228
- const resolved = path6.resolve(projectRoot, inputCwd);
2229
- const normalizedResolved = path6.normalize(resolved).toLowerCase();
2230
- const normalizedRoot = path6.normalize(projectRoot).toLowerCase();
2692
+ const resolved = path8.resolve(projectRoot, inputCwd);
2693
+ const normalizedResolved = path8.normalize(resolved).toLowerCase();
2694
+ const normalizedRoot = path8.normalize(projectRoot).toLowerCase();
2231
2695
  if (!normalizedResolved.startsWith(normalizedRoot)) {
2232
2696
  return `\u{1F6AB} BLOCKED: Path "${inputCwd}" resolves outside project root "${projectRoot}".`;
2233
2697
  }
2234
- if (!fs6.existsSync(resolved)) {
2698
+ if (!fs7.existsSync(resolved)) {
2235
2699
  return `\u26A0\uFE0F Directory "${inputCwd}" does not exist at "${resolved}".`;
2236
2700
  }
2237
2701
  workingDir = resolved;
@@ -2317,9 +2781,9 @@ function formatTimeoutResult(command, timeout) {
2317
2781
  }
2318
2782
 
2319
2783
  // src/agents/codeReviewer.ts
2320
- import fs7 from "fs";
2321
- import path7 from "path";
2322
- import { execSync as execSync2 } from "child_process";
2784
+ import fs8 from "fs";
2785
+ import path9 from "path";
2786
+ import { execSync as execSync4 } from "child_process";
2323
2787
  var CODE_EXTENSIONS = [
2324
2788
  ".ts",
2325
2789
  ".tsx",
@@ -2349,7 +2813,7 @@ var CONFIG_EXTENSIONS = [
2349
2813
  function getGitChangedFiles() {
2350
2814
  try {
2351
2815
  const root = getProjectRoot();
2352
- const stdout = execSync2("git status --porcelain", {
2816
+ const stdout = execSync4("git status --porcelain", {
2353
2817
  cwd: root,
2354
2818
  encoding: "utf-8"
2355
2819
  });
@@ -2362,7 +2826,7 @@ function getGitChangedFiles() {
2362
2826
  if (filePath.startsWith('"') && filePath.endsWith('"')) {
2363
2827
  filePath = filePath.substring(1, filePath.length - 1);
2364
2828
  }
2365
- const ext = path7.extname(filePath).toLowerCase();
2829
+ const ext = path9.extname(filePath).toLowerCase();
2366
2830
  if (CODE_EXTENSIONS.includes(ext)) files.push(filePath);
2367
2831
  }
2368
2832
  return files.slice(0, 10);
@@ -2399,7 +2863,7 @@ Pass an explicit 'files' array to review specific files.`;
2399
2863
  continue;
2400
2864
  }
2401
2865
  const resolvedPath = validation.resolvedPath;
2402
- if (!fs7.existsSync(resolvedPath)) {
2866
+ if (!fs8.existsSync(resolvedPath)) {
2403
2867
  allIssues.push({
2404
2868
  file: filePath,
2405
2869
  line: 0,
@@ -2411,7 +2875,7 @@ Pass an explicit 'files' array to review specific files.`;
2411
2875
  continue;
2412
2876
  }
2413
2877
  try {
2414
- const content = fs7.readFileSync(resolvedPath, "utf-8");
2878
+ const content = fs8.readFileSync(resolvedPath, "utf-8");
2415
2879
  filesReviewed++;
2416
2880
  checkGeneral(filePath, content, allIssues);
2417
2881
  switch (focus) {
@@ -2509,12 +2973,12 @@ function isTestFile(filePath) {
2509
2973
  return /\.(test|spec)\.[a-z]+$/i.test(filePath) || /__tests__/.test(filePath);
2510
2974
  }
2511
2975
  function isTsLike(filePath) {
2512
- const ext = path7.extname(filePath).toLowerCase();
2976
+ const ext = path9.extname(filePath).toLowerCase();
2513
2977
  return ext === ".ts" || ext === ".tsx";
2514
2978
  }
2515
2979
  function checkGeneral(filePath, content, issues) {
2516
2980
  const lines = content.split("\n");
2517
- const ext = path7.extname(filePath).toLowerCase();
2981
+ const ext = path9.extname(filePath).toLowerCase();
2518
2982
  if (content.length > 0 && !content.endsWith("\n")) {
2519
2983
  issues.push({
2520
2984
  file: filePath,
@@ -2716,7 +3180,7 @@ function stripJsonComments(text) {
2716
3180
  return cleaned.join("\n");
2717
3181
  }
2718
3182
  function checkConfigFile(filePath, content, issues) {
2719
- const ext = path7.extname(filePath).toLowerCase();
3183
+ const ext = path9.extname(filePath).toLowerCase();
2720
3184
  const lines = content.split("\n");
2721
3185
  if (ext === ".json" || ext === ".jsonc") {
2722
3186
  checkJson(filePath, content, lines, issues);
@@ -2727,7 +3191,7 @@ function checkConfigFile(filePath, content, issues) {
2727
3191
  }
2728
3192
  }
2729
3193
  function checkJson(filePath, content, lines, issues) {
2730
- const ext = path7.extname(filePath).toLowerCase();
3194
+ const ext = path9.extname(filePath).toLowerCase();
2731
3195
  const isJsonc = ext === ".jsonc";
2732
3196
  const contentToParse = isJsonc ? stripJsonComments(content) : content;
2733
3197
  try {
@@ -3453,10 +3917,10 @@ function formatConventionsOutput(conventions) {
3453
3917
  }
3454
3918
 
3455
3919
  // src/utils/gitUtils.ts
3456
- import { execSync as execSync3 } from "child_process";
3920
+ import { execSync as execSync5 } from "child_process";
3457
3921
  function runGitCommand(command) {
3458
3922
  const root = getProjectRoot();
3459
- return execSync3(command, {
3923
+ return execSync5(command, {
3460
3924
  cwd: root,
3461
3925
  encoding: "utf-8",
3462
3926
  maxBuffer: 2 * 1024 * 1024
@@ -3584,8 +4048,8 @@ async function handleGitDiff(params) {
3584
4048
  }
3585
4049
 
3586
4050
  // src/tools/projectStructure.ts
3587
- import fs8 from "fs";
3588
- import path8 from "path";
4051
+ import fs9 from "fs";
4052
+ import path10 from "path";
3589
4053
  var DEFAULT_IGNORE = [
3590
4054
  "node_modules",
3591
4055
  ".git",
@@ -3603,7 +4067,7 @@ var DEFAULT_IGNORE = [
3603
4067
  "*.log"
3604
4068
  ];
3605
4069
  var treeCache = /* @__PURE__ */ new Map();
3606
- var CACHE_TTL_MS2 = 6e4;
4070
+ var CACHE_TTL_MS3 = 6e4;
3607
4071
  async function handleProjectStructure(params) {
3608
4072
  const {
3609
4073
  depth = 3,
@@ -3615,14 +4079,14 @@ async function handleProjectStructure(params) {
3615
4079
  const clampedDepth = Math.max(1, Math.min(depth, 6));
3616
4080
  const cacheKey = `${root}:${clampedDepth}:${folderOnly}:${includePattern ?? ""}:${excludePattern ?? ""}`;
3617
4081
  const cached = treeCache.get(cacheKey);
3618
- if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS2) {
4082
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS3) {
3619
4083
  sessionMemory.recordToolCall("project_structure", { depth: clampedDepth, folderOnly, cached: true });
3620
4084
  return cached.result;
3621
4085
  }
3622
4086
  sessionMemory.recordToolCall("project_structure", { depth: clampedDepth, folderOnly });
3623
4087
  try {
3624
4088
  const tree = buildTree(root, root, clampedDepth, 0, folderOnly, includePattern, excludePattern);
3625
- const projectName = path8.basename(root);
4089
+ const projectName = path10.basename(root);
3626
4090
  const lines = [
3627
4091
  "[Project Structure] - " + projectName,
3628
4092
  "Depth: " + clampedDepth + " | " + (folderOnly ? "Folders only" : "Files and folders"),
@@ -3645,7 +4109,7 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3645
4109
  const lines = [];
3646
4110
  let entries = [];
3647
4111
  try {
3648
- entries = fs8.readdirSync(currentDir, { withFileTypes: true });
4112
+ entries = fs9.readdirSync(currentDir, { withFileTypes: true });
3649
4113
  } catch {
3650
4114
  return lines;
3651
4115
  }
@@ -3654,12 +4118,12 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3654
4118
  if (!a.isDirectory() && b.isDirectory()) return 1;
3655
4119
  return a.name.localeCompare(b.name);
3656
4120
  });
3657
- const relativeDir = path8.relative(root, currentDir) || ".";
4121
+ const relativeDir = path10.relative(root, currentDir) || ".";
3658
4122
  const prefix = getPrefix(currentDepth);
3659
4123
  for (const entry of entries) {
3660
4124
  if (shouldIgnore(entry.name, relativeDir, root)) continue;
3661
- const fullPath = path8.join(currentDir, entry.name);
3662
- const relativePath = path8.relative(root, fullPath);
4125
+ const fullPath = path10.join(currentDir, entry.name);
4126
+ const relativePath = path10.relative(root, fullPath);
3663
4127
  if (includePattern && !entry.name.includes(includePattern) && !relativePath.includes(includePattern)) {
3664
4128
  if (!entry.isDirectory()) continue;
3665
4129
  }
@@ -3670,11 +4134,11 @@ function buildTree(root, currentDir, maxDepth, currentDepth, folderOnly, include
3670
4134
  lines.push(prefix + "[D] " + entry.name + "/");
3671
4135
  let subEntries = [];
3672
4136
  try {
3673
- subEntries = fs8.readdirSync(fullPath, { withFileTypes: true });
4137
+ subEntries = fs9.readdirSync(fullPath, { withFileTypes: true });
3674
4138
  } catch {
3675
4139
  }
3676
4140
  const hasVisibleContent = subEntries.some(
3677
- (e) => !shouldIgnore(e.name, path8.relative(root, fullPath), root)
4141
+ (e) => !shouldIgnore(e.name, path10.relative(root, fullPath), root)
3678
4142
  );
3679
4143
  if (hasVisibleContent) {
3680
4144
  const subLines = buildTree(root, fullPath, maxDepth, currentDepth + 1, folderOnly, includePattern, excludePattern);
@@ -3705,7 +4169,7 @@ function getPrefix(depth) {
3705
4169
  }
3706
4170
  function getFileSize(fullPath) {
3707
4171
  try {
3708
- const stat = fs8.statSync(fullPath);
4172
+ const stat = fs9.statSync(fullPath);
3709
4173
  if (stat.size < 1024) return stat.size + "B";
3710
4174
  if (stat.size < 1024 * 1024) return (stat.size / 1024).toFixed(0) + "KB";
3711
4175
  const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
@@ -3716,8 +4180,8 @@ function getFileSize(fullPath) {
3716
4180
  }
3717
4181
 
3718
4182
  // src/tools/staticAnalysis.ts
3719
- import fs9 from "fs";
3720
- import path9 from "path";
4183
+ import fs10 from "fs";
4184
+ import path11 from "path";
3721
4185
  async function handleStaticAnalysis(params) {
3722
4186
  const { tool = "all", files, autoFix = false, timeout = 60 } = params;
3723
4187
  const root = getProjectRoot();
@@ -3810,11 +4274,11 @@ function detectAvailableTools(root) {
3810
4274
  "eslint.config.js",
3811
4275
  "eslint.config.mjs"
3812
4276
  ];
3813
- const hasEslintConfig = eslintConfigs.some((cfg) => fs9.existsSync(path9.join(root, cfg)));
4277
+ const hasEslintConfig = eslintConfigs.some((cfg) => fs10.existsSync(path11.join(root, cfg)));
3814
4278
  if (hasEslintConfig && depNames.has("eslint")) {
3815
4279
  tools.push("eslint");
3816
4280
  }
3817
- if (fs9.existsSync(path9.join(root, "tsconfig.json")) && depNames.has("typescript")) {
4281
+ if (fs10.existsSync(path11.join(root, "tsconfig.json")) && depNames.has("typescript")) {
3818
4282
  tools.push("tsc");
3819
4283
  }
3820
4284
  const prettierConfigs = [
@@ -3825,20 +4289,20 @@ function detectAvailableTools(root) {
3825
4289
  ".prettierrc.toml",
3826
4290
  "prettier.config.js"
3827
4291
  ];
3828
- const hasPrettierConfig = prettierConfigs.some((cfg) => fs9.existsSync(path9.join(root, cfg)));
4292
+ const hasPrettierConfig = prettierConfigs.some((cfg) => fs10.existsSync(path11.join(root, cfg)));
3829
4293
  if (hasPrettierConfig && depNames.has("prettier")) {
3830
4294
  tools.push("prettier");
3831
4295
  }
3832
- if (fs9.existsSync(path9.join(root, "ruff.toml")) || fs9.existsSync(path9.join(root, ".ruff.toml"))) {
4296
+ if (fs10.existsSync(path11.join(root, "ruff.toml")) || fs10.existsSync(path11.join(root, ".ruff.toml"))) {
3833
4297
  tools.push("ruff");
3834
4298
  }
3835
4299
  return tools;
3836
4300
  }
3837
4301
  function readPackageJson(root) {
3838
4302
  try {
3839
- const pkgPath = path9.join(root, "package.json");
3840
- if (fs9.existsSync(pkgPath)) {
3841
- return JSON.parse(fs9.readFileSync(pkgPath, "utf-8"));
4303
+ const pkgPath = path11.join(root, "package.json");
4304
+ if (fs10.existsSync(pkgPath)) {
4305
+ return JSON.parse(fs10.readFileSync(pkgPath, "utf-8"));
3842
4306
  }
3843
4307
  } catch {
3844
4308
  }
@@ -3894,18 +4358,18 @@ function buildToolCommand(tool, files, autoFix) {
3894
4358
  }
3895
4359
  function detectPackageManagerPrefix() {
3896
4360
  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 ";
4361
+ if (fs10.existsSync(path11.join(root, "pnpm-lock.yaml"))) return "pnpm ";
4362
+ if (fs10.existsSync(path11.join(root, "yarn.lock"))) return "yarn ";
3899
4363
  return "npx --no-install ";
3900
4364
  }
3901
4365
  function findBinary(root, binName) {
3902
4366
  const candidates = [
3903
- path9.join(root, "node_modules", ".bin", binName),
3904
- path9.join(root, "..", "node_modules", ".bin", binName)
4367
+ path11.join(root, "node_modules", ".bin", binName),
4368
+ path11.join(root, "..", "node_modules", ".bin", binName)
3905
4369
  ];
3906
4370
  for (const candidate of candidates) {
3907
4371
  try {
3908
- if (fs9.existsSync(candidate)) {
4372
+ if (fs10.existsSync(candidate)) {
3909
4373
  return candidate;
3910
4374
  }
3911
4375
  } catch {
@@ -3929,7 +4393,7 @@ function parseToolOutput(tool, stdout, stderr, projectRoot) {
3929
4393
  }
3930
4394
  function resolveToolPath(filePath, projectRoot) {
3931
4395
  const trimmed = filePath.trim();
3932
- return path9.isAbsolute(trimmed) ? path9.relative(projectRoot, trimmed) : trimmed.replace(/^\.\//, "");
4396
+ return path11.isAbsolute(trimmed) ? path11.relative(projectRoot, trimmed) : trimmed.replace(/^\.\//, "");
3933
4397
  }
3934
4398
  function parseEslintOutput(output, projectRoot) {
3935
4399
  const issues = [];
@@ -4112,7 +4576,7 @@ function formatToolNotAvailable(requested, available) {
4112
4576
  }
4113
4577
 
4114
4578
  // src/utils/kumaShared.ts
4115
- import { execSync as execSync4 } from "child_process";
4579
+ import { execSync as execSync6 } from "child_process";
4116
4580
  function getSessionStats(inputGoal) {
4117
4581
  const summary = sessionMemory.getSummary();
4118
4582
  const goal = inputGoal || summary.currentGoal || "";
@@ -4134,7 +4598,7 @@ function getSessionStats(inputGoal) {
4134
4598
  function getGitDiffStat(timeout = 3e3) {
4135
4599
  try {
4136
4600
  const root = getProjectRoot();
4137
- return execSync4("git diff --stat", {
4601
+ return execSync6("git diff --stat", {
4138
4602
  cwd: root,
4139
4603
  encoding: "utf-8",
4140
4604
  timeout
@@ -4407,8 +4871,8 @@ function formatAnalytics(a) {
4407
4871
  }
4408
4872
 
4409
4873
  // src/engine/kumaHealthDashboard.ts
4410
- import fs10 from "fs";
4411
- import path10 from "path";
4874
+ import fs11 from "fs";
4875
+ import path12 from "path";
4412
4876
  function computeHealthDashboard() {
4413
4877
  const analytics = computeAnalytics();
4414
4878
  const root = getProjectRoot();
@@ -4416,27 +4880,27 @@ function computeHealthDashboard() {
4416
4880
  const gitChanges = gitStat ? gitStat.split("\n").filter((l) => l.includes("|")).length : 0;
4417
4881
  let backupCount = 0;
4418
4882
  try {
4419
- const bd = path10.join(root, ".kuma", "backups");
4420
- if (fs10.existsSync(bd)) backupCount = fs10.readdirSync(bd).filter((d) => /^\d+$/.test(d)).length;
4883
+ const bd = path12.join(root, ".kuma", "backups");
4884
+ if (fs11.existsSync(bd)) backupCount = fs11.readdirSync(bd).filter((d) => /^\d+$/.test(d)).length;
4421
4885
  } catch {
4422
4886
  }
4423
4887
  const dirFileCounts = /* @__PURE__ */ new Map();
4424
4888
  try {
4425
4889
  const walk = (dir, base) => {
4426
4890
  try {
4427
- for (const e of fs10.readdirSync(dir, { withFileTypes: true })) {
4891
+ for (const e of fs11.readdirSync(dir, { withFileTypes: true })) {
4428
4892
  if (e.name.startsWith(".") || e.name === "node_modules") continue;
4429
- const full = path10.join(dir, e.name);
4893
+ const full = path12.join(dir, e.name);
4430
4894
  if (e.isDirectory()) walk(full, base);
4431
4895
  else if (e.isFile() && /\.(ts|tsx|js|jsx)$/.test(e.name)) {
4432
- const rel = path10.dirname(path10.relative(base, full));
4896
+ const rel = path12.dirname(path12.relative(base, full));
4433
4897
  dirFileCounts.set(rel, (dirFileCounts.get(rel) || 0) + 1);
4434
4898
  }
4435
4899
  }
4436
4900
  } catch {
4437
4901
  }
4438
4902
  };
4439
- walk(path10.join(root, "src"), root);
4903
+ walk(path12.join(root, "src"), root);
4440
4904
  } catch {
4441
4905
  }
4442
4906
  const dirMap = /* @__PURE__ */ new Map();
@@ -4444,7 +4908,7 @@ function computeHealthDashboard() {
4444
4908
  if (call.toolName === "precise_diff_editor") {
4445
4909
  const fp = call.params?.filePath;
4446
4910
  if (fp) {
4447
- const dir = path10.dirname(fp) || ".";
4911
+ const dir = path12.dirname(fp) || ".";
4448
4912
  const e = dirMap.get(dir) || { edits: 0, failures: 0 };
4449
4913
  e.edits++;
4450
4914
  if (call.params?.success === false) e.failures++;
@@ -4524,9 +4988,9 @@ function formatHealthDashboard(r) {
4524
4988
  }
4525
4989
 
4526
4990
  // src/guards/antiPatternDetector.ts
4527
- import fs11 from "fs";
4528
- import path11 from "path";
4529
- import { execSync as execSync5 } from "child_process";
4991
+ import fs12 from "fs";
4992
+ import path13 from "path";
4993
+ import { execSync as execSync7 } from "child_process";
4530
4994
  var SCRIPT_PATCH_PATTERNS = [
4531
4995
  "writeFileSync",
4532
4996
  "writeFile",
@@ -4552,20 +5016,20 @@ function findPatchScripts(projectRoot) {
4552
5016
  const warnings = [];
4553
5017
  const recentFiles = scanRecentFiles(projectRoot);
4554
5018
  for (const file of recentFiles) {
4555
- const ext = path11.extname(file).toLowerCase();
5019
+ const ext = path13.extname(file).toLowerCase();
4556
5020
  if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
4557
- const relativePath = path11.relative(projectRoot, file);
5021
+ const relativePath = path13.relative(projectRoot, file);
4558
5022
  if (relativePath.startsWith("src") || relativePath.startsWith("test") || relativePath.startsWith("node_modules") || relativePath.startsWith(".")) {
4559
5023
  continue;
4560
5024
  }
4561
5025
  try {
4562
- const content = fs11.readFileSync(file, "utf-8").toLowerCase();
5026
+ const content = fs12.readFileSync(file, "utf-8").toLowerCase();
4563
5027
  const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
4564
5028
  if (matchedPattern) {
4565
5029
  warnings.push({
4566
5030
  severity: "high",
4567
5031
  pattern: "script-patching",
4568
- message: `Created script file that modifies other files: ${path11.basename(file)}`,
5032
+ message: `Created script file that modifies other files: ${path13.basename(file)}`,
4569
5033
  suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
4570
5034
  evidence: `File: ${relativePath} contains '${matchedPattern}'`,
4571
5035
  filePath: relativePath
@@ -4599,15 +5063,15 @@ function detectBashGrepUsage() {
4599
5063
  function scanRecentFiles(projectRoot) {
4600
5064
  const recent = [];
4601
5065
  try {
4602
- const entries = fs11.readdirSync(projectRoot, { withFileTypes: true });
5066
+ const entries = fs12.readdirSync(projectRoot, { withFileTypes: true });
4603
5067
  const now = Date.now();
4604
5068
  const recentThreshold = 30 * 60 * 1e3;
4605
5069
  for (const entry of entries) {
4606
5070
  if (!entry.isFile()) continue;
4607
5071
  try {
4608
- const stat = fs11.statSync(path11.join(projectRoot, entry.name));
5072
+ const stat = fs12.statSync(path13.join(projectRoot, entry.name));
4609
5073
  if (now - stat.mtimeMs < recentThreshold || now - stat.ctimeMs < recentThreshold) {
4610
- recent.push(path11.join(projectRoot, entry.name));
5074
+ recent.push(path13.join(projectRoot, entry.name));
4611
5075
  }
4612
5076
  } catch {
4613
5077
  continue;
@@ -4620,7 +5084,7 @@ function scanRecentFiles(projectRoot) {
4620
5084
  function detectGitPatchScripts(projectRoot) {
4621
5085
  const warnings = [];
4622
5086
  try {
4623
- const statusStdout = execSync5("git status --porcelain", {
5087
+ const statusStdout = execSync7("git status --porcelain", {
4624
5088
  cwd: projectRoot,
4625
5089
  encoding: "utf-8",
4626
5090
  timeout: 3e3
@@ -4631,14 +5095,14 @@ function detectGitPatchScripts(projectRoot) {
4631
5095
  const prefix = line.substring(0, 2);
4632
5096
  if (prefix !== "??" && prefix !== "A ") continue;
4633
5097
  const file = line.substring(3).trim();
4634
- const ext = path11.extname(file).toLowerCase();
5098
+ const ext = path13.extname(file).toLowerCase();
4635
5099
  if (!SCRIPT_EXTENSIONS.includes(ext)) continue;
4636
5100
  const isRootLevel = !file.includes("/");
4637
5101
  const isScriptsDir = file.startsWith("scripts/") || file.startsWith("patches/");
4638
5102
  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();
5103
+ const fullPath = path13.join(projectRoot, file);
5104
+ if (fs12.existsSync(fullPath)) {
5105
+ const content = fs12.readFileSync(fullPath, "utf-8").toLowerCase();
4642
5106
  const matchedPattern = SCRIPT_PATCH_PATTERNS.find((p) => content.includes(p.toLowerCase()));
4643
5107
  if (matchedPattern) {
4644
5108
  warnings.push({
@@ -4653,7 +5117,7 @@ function detectGitPatchScripts(projectRoot) {
4653
5117
  }
4654
5118
  }
4655
5119
  }
4656
- const deletedStdout = execSync5("git diff --name-only --diff-filter=D HEAD", {
5120
+ const deletedStdout = execSync7("git diff --name-only --diff-filter=D HEAD", {
4657
5121
  cwd: projectRoot,
4658
5122
  encoding: "utf-8",
4659
5123
  timeout: 3e3
@@ -4661,7 +5125,7 @@ function detectGitPatchScripts(projectRoot) {
4661
5125
  if (deletedStdout) {
4662
5126
  const deletedFiles = deletedStdout.split("\n").filter(Boolean);
4663
5127
  for (const file of deletedFiles) {
4664
- const ext = path11.extname(file).toLowerCase();
5128
+ const ext = path13.extname(file).toLowerCase();
4665
5129
  if (SCRIPT_EXTENSIONS.includes(ext)) {
4666
5130
  warnings.push({
4667
5131
  severity: "high",
@@ -4721,21 +5185,21 @@ function detectAllAntiPatterns() {
4721
5185
  }
4722
5186
 
4723
5187
  // src/engine/contextSnapshot.ts
4724
- import fs12 from "fs";
4725
- import path12 from "path";
4726
- import { execSync as execSync6 } from "child_process";
5188
+ import fs13 from "fs";
5189
+ import path14 from "path";
5190
+ import { execSync as execSync8 } from "child_process";
4727
5191
  var SNAPSHOT_DIR = "context-snapshots";
4728
5192
  function snapshotDir() {
4729
- return path12.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
5193
+ return path14.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
4730
5194
  }
4731
5195
  function ensureSnapshotDir() {
4732
5196
  const dir = snapshotDir();
4733
- if (!fs12.existsSync(dir)) {
4734
- fs12.mkdirSync(dir, { recursive: true });
5197
+ if (!fs13.existsSync(dir)) {
5198
+ fs13.mkdirSync(dir, { recursive: true });
4735
5199
  }
4736
5200
  }
4737
5201
  function snapshotFilePath(timestamp) {
4738
- return path12.join(snapshotDir(), `${timestamp}.json`);
5202
+ return path14.join(snapshotDir(), `${timestamp}.json`);
4739
5203
  }
4740
5204
  function saveSnapshot(goal) {
4741
5205
  try {
@@ -4756,7 +5220,7 @@ function saveSnapshot(goal) {
4756
5220
  hasConventions: !!summary.hasConventions
4757
5221
  };
4758
5222
  try {
4759
- fs12.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
5223
+ fs13.writeFileSync(snapshotFilePath(Date.now()), JSON.stringify(snapshot, null, 2), "utf-8");
4760
5224
  } catch {
4761
5225
  }
4762
5226
  sessionMemory.recordToolCall("kuma_context", { action: "save", goal: snapshot.goal });
@@ -4764,12 +5228,12 @@ function saveSnapshot(goal) {
4764
5228
  }
4765
5229
  function listSnapshots() {
4766
5230
  const dir = snapshotDir();
4767
- if (!fs12.existsSync(dir)) return [];
5231
+ if (!fs13.existsSync(dir)) return [];
4768
5232
  try {
4769
- const files = fs12.readdirSync(dir).filter((f) => f.endsWith(".json")).sort((a, b) => b.localeCompare(a));
5233
+ const files = fs13.readdirSync(dir).filter((f) => f.endsWith(".json")).sort((a, b) => b.localeCompare(a));
4770
5234
  return files.map((f) => {
4771
5235
  try {
4772
- const content = fs12.readFileSync(path12.join(dir, f), "utf-8");
5236
+ const content = fs13.readFileSync(path14.join(dir, f), "utf-8");
4773
5237
  return JSON.parse(content);
4774
5238
  } catch {
4775
5239
  return null;
@@ -4812,7 +5276,7 @@ function formatSnapshot(snapshot) {
4812
5276
  function getGitDiffStat2() {
4813
5277
  try {
4814
5278
  const root = getProjectRoot();
4815
- const stdout = execSync6("git diff --stat", {
5279
+ const stdout = execSync8("git diff --stat", {
4816
5280
  cwd: root,
4817
5281
  encoding: "utf-8",
4818
5282
  timeout: 3e3
@@ -4957,25 +5421,25 @@ async function handleKumaContext(params) {
4957
5421
  }
4958
5422
 
4959
5423
  // src/tools/kumaInit.ts
4960
- import fs13 from "fs";
4961
- import path13 from "path";
5424
+ import fs14 from "fs";
5425
+ import path15 from "path";
4962
5426
  async function handleKumaInit(params) {
4963
5427
  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");
5428
+ const kumaDir = path15.join(root, ".kuma");
5429
+ const memoriesDir = path15.join(kumaDir, "memories");
5430
+ const initMdPath = path15.join(kumaDir, "init.md");
4967
5431
  sessionMemory.recordToolCall("kuma_init", { projectRoot: root });
4968
5432
  let rules = "";
4969
- if (fs13.existsSync(initMdPath)) {
4970
- rules = fs13.readFileSync(initMdPath, "utf-8");
5433
+ if (fs14.existsSync(initMdPath)) {
5434
+ rules = fs14.readFileSync(initMdPath, "utf-8");
4971
5435
  }
4972
5436
  const memories = [];
4973
- if (fs13.existsSync(memoriesDir)) {
5437
+ if (fs14.existsSync(memoriesDir)) {
4974
5438
  try {
4975
- const files = fs13.readdirSync(memoriesDir).filter((f) => f.endsWith(".md"));
5439
+ const files = fs14.readdirSync(memoriesDir).filter((f) => f.endsWith(".md"));
4976
5440
  for (const file of files) {
4977
5441
  try {
4978
- const content = fs13.readFileSync(path13.join(memoriesDir, file), "utf-8");
5442
+ const content = fs14.readFileSync(path15.join(memoriesDir, file), "utf-8");
4979
5443
  memories.push({ topic: file.replace(/\.md$/, ""), content });
4980
5444
  } catch {
4981
5445
  }
@@ -5019,9 +5483,9 @@ ${memoryList}`);
5019
5483
  }
5020
5484
 
5021
5485
  // src/tools/kumaRisk.ts
5022
- import path14 from "path";
5023
- import fg2 from "fast-glob";
5024
- import fs14 from "fs";
5486
+ import path16 from "path";
5487
+ import fg4 from "fast-glob";
5488
+ import fs15 from "fs";
5025
5489
  async function handleKumaRisk(params) {
5026
5490
  const { symbol, filePath, depth = 2 } = params;
5027
5491
  if (!symbol && !filePath) {
@@ -5055,7 +5519,7 @@ async function handleKumaRisk(params) {
5055
5519
  ];
5056
5520
  let entries = [];
5057
5521
  try {
5058
- entries = await fg2("**/*.{ts,tsx,js,jsx,mjs,cjs}", {
5522
+ entries = await fg4("**/*.{ts,tsx,js,jsx,mjs,cjs}", {
5059
5523
  cwd: root,
5060
5524
  ignore: ignorePatterns,
5061
5525
  onlyFiles: true,
@@ -5074,11 +5538,11 @@ async function handleKumaRisk(params) {
5074
5538
  for (const entry of entries) {
5075
5539
  if (results.length >= maxResults) break;
5076
5540
  try {
5077
- const fullPath = path14.join(root, entry);
5078
- const stat = fs14.statSync(fullPath);
5541
+ const fullPath = path16.join(root, entry);
5542
+ const stat = fs15.statSync(fullPath);
5079
5543
  if (stat.size > 5e5) continue;
5080
5544
  if (isBinaryFile(fullPath)) continue;
5081
- const content = fs14.readFileSync(fullPath, "utf-8");
5545
+ const content = fs15.readFileSync(fullPath, "utf-8");
5082
5546
  const lines = content.split("\n");
5083
5547
  for (let i = 0; i < lines.length; i++) {
5084
5548
  if (results.length >= maxResults) break;
@@ -5202,8 +5666,8 @@ function formatRiskReport(report, circularDepWarning) {
5202
5666
  }
5203
5667
 
5204
5668
  // src/tools/kumaDependencyGuard.ts
5205
- import fs15 from "fs";
5206
- import path15 from "path";
5669
+ import fs16 from "fs";
5670
+ import path17 from "path";
5207
5671
  var NATIVE_JS_ALTERNATIVES = {
5208
5672
  "lodash": [
5209
5673
  { method: "Array.prototype.map()", description: "Replace _.map()" },
@@ -5290,12 +5754,12 @@ var NATIVE_JS_ALTERNATIVES = {
5290
5754
  };
5291
5755
  function findExistingDependency(packageName) {
5292
5756
  const root = getProjectRoot();
5293
- const packageJsonPath = path15.join(root, "package.json");
5294
- if (!fs15.existsSync(packageJsonPath)) {
5757
+ const packageJsonPath = path17.join(root, "package.json");
5758
+ if (!fs16.existsSync(packageJsonPath)) {
5295
5759
  return { found: false };
5296
5760
  }
5297
5761
  try {
5298
- const pkg = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
5762
+ const pkg = JSON.parse(fs16.readFileSync(packageJsonPath, "utf-8"));
5299
5763
  const deps = pkg.dependencies || {};
5300
5764
  if (deps[packageName]) {
5301
5765
  return { found: true, version: deps[packageName] };
@@ -5311,12 +5775,12 @@ function findExistingDependency(packageName) {
5311
5775
  }
5312
5776
  function findSimilarExisting(packageName) {
5313
5777
  const root = getProjectRoot();
5314
- const packageJsonPath = path15.join(root, "package.json");
5315
- if (!fs15.existsSync(packageJsonPath)) {
5778
+ const packageJsonPath = path17.join(root, "package.json");
5779
+ if (!fs16.existsSync(packageJsonPath)) {
5316
5780
  return [];
5317
5781
  }
5318
5782
  try {
5319
- const pkg = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
5783
+ const pkg = JSON.parse(fs16.readFileSync(packageJsonPath, "utf-8"));
5320
5784
  const allDeps = { ...pkg.dependencies || {}, ...pkg.devDependencies || {} };
5321
5785
  const installedPackages = Object.keys(allDeps);
5322
5786
  const alternativeMap = {
@@ -5427,8 +5891,8 @@ function getEstimatedPackageSize(packageName) {
5427
5891
  }
5428
5892
 
5429
5893
  // src/tools/kumaPolicy.ts
5430
- import fs16 from "fs";
5431
- import path16 from "path";
5894
+ import fs17 from "fs";
5895
+ import path18 from "path";
5432
5896
  var DEFAULT_POLICY = {
5433
5897
  never_touch: [
5434
5898
  ".env",
@@ -5454,15 +5918,15 @@ var DEFAULT_POLICY = {
5454
5918
  };
5455
5919
  function loadPolicy() {
5456
5920
  const root = getProjectRoot();
5457
- const ymlPath = path16.join(root, ".kuma", "policy.yml");
5458
- const yamlPath = path16.join(root, ".kuma", "policy.yaml");
5921
+ const ymlPath = path18.join(root, ".kuma", "policy.yml");
5922
+ const yamlPath = path18.join(root, ".kuma", "policy.yaml");
5459
5923
  let policyContent = null;
5460
5924
  let policyPath = null;
5461
- if (fs16.existsSync(ymlPath)) {
5462
- policyContent = fs16.readFileSync(ymlPath, "utf-8");
5925
+ if (fs17.existsSync(ymlPath)) {
5926
+ policyContent = fs17.readFileSync(ymlPath, "utf-8");
5463
5927
  policyPath = ymlPath;
5464
- } else if (fs16.existsSync(yamlPath)) {
5465
- policyContent = fs16.readFileSync(yamlPath, "utf-8");
5928
+ } else if (fs17.existsSync(yamlPath)) {
5929
+ policyContent = fs17.readFileSync(yamlPath, "utf-8");
5466
5930
  policyPath = yamlPath;
5467
5931
  }
5468
5932
  if (!policyContent) {
@@ -5579,9 +6043,9 @@ function checkFilePathPolicy(filePath, policy) {
5579
6043
  if (policy.max_file_size && policy.max_file_size > 0) {
5580
6044
  try {
5581
6045
  const root = getProjectRoot();
5582
- const fullPath = path16.join(root, filePath);
5583
- if (fs16.existsSync(fullPath)) {
5584
- const sizeKB = fs16.statSync(fullPath).size / 1024;
6046
+ const fullPath = path18.join(root, filePath);
6047
+ if (fs17.existsSync(fullPath)) {
6048
+ const sizeKB = fs17.statSync(fullPath).size / 1024;
5585
6049
  if (sizeKB > policy.max_file_size) {
5586
6050
  violations.push({
5587
6051
  rule: "max_file_size",
@@ -5677,8 +6141,8 @@ function formatPolicyResult(type, value, result, policy) {
5677
6141
  }
5678
6142
 
5679
6143
  // src/engine/safetyScore.ts
5680
- import fs17 from "fs";
5681
- import path17 from "path";
6144
+ import fs18 from "fs";
6145
+ import path19 from "path";
5682
6146
  function computeSafetyScore(inputGoal) {
5683
6147
  const stats = getSessionStats(inputGoal);
5684
6148
  const checks = [];
@@ -5722,8 +6186,8 @@ function computeSafetyScore(inputGoal) {
5722
6186
  totalScore += 20;
5723
6187
  }
5724
6188
  const backupDir = getKumaBackupsDir();
5725
- if (fs17.existsSync(backupDir)) {
5726
- const backupCount = fs17.readdirSync(backupDir).filter((d) => /^\d+$/.test(d)).length;
6189
+ if (fs18.existsSync(backupDir)) {
6190
+ const backupCount = fs18.readdirSync(backupDir).filter((d) => /^\d+$/.test(d)).length;
5727
6191
  checks.push({
5728
6192
  label: "Backup Available",
5729
6193
  status: "pass",
@@ -5741,8 +6205,8 @@ function computeSafetyScore(inputGoal) {
5741
6205
  totalScore += 5;
5742
6206
  }
5743
6207
  try {
5744
- const lspPath = path17.join(getProjectRoot(), "node_modules", ".bin", "typescript-language-server");
5745
- if (fs17.existsSync(lspPath)) {
6208
+ const lspPath = path19.join(getProjectRoot(), "node_modules", ".bin", "typescript-language-server");
6209
+ if (fs18.existsSync(lspPath)) {
5746
6210
  checks.push({
5747
6211
  label: "LSP Available",
5748
6212
  status: "pass",
@@ -5964,8 +6428,8 @@ function formatSafetyScore(report) {
5964
6428
 
5965
6429
  // src/engine/kumaDb.ts
5966
6430
  import initSqlJs from "sql.js";
5967
- import fs18 from "fs";
5968
- import path18 from "path";
6431
+ import fs19 from "fs";
6432
+ import path20 from "path";
5969
6433
  var DB_FILENAME = "kuma.db";
5970
6434
  var dbInstance = null;
5971
6435
  var initPromise = null;
@@ -5978,13 +6442,13 @@ async function getDb() {
5978
6442
  async function initDb() {
5979
6443
  const SQL = await initSqlJs();
5980
6444
  const kumaDir = getKumaDir();
5981
- const dbPath = path18.join(kumaDir, DB_FILENAME);
5982
- if (!fs18.existsSync(kumaDir)) {
5983
- fs18.mkdirSync(kumaDir, { recursive: true });
6445
+ const dbPath = path20.join(kumaDir, DB_FILENAME);
6446
+ if (!fs19.existsSync(kumaDir)) {
6447
+ fs19.mkdirSync(kumaDir, { recursive: true });
5984
6448
  }
5985
6449
  let db;
5986
- if (fs18.existsSync(dbPath)) {
5987
- const buffer = fs18.readFileSync(dbPath);
6450
+ if (fs19.existsSync(dbPath)) {
6451
+ const buffer = fs19.readFileSync(dbPath);
5988
6452
  db = new SQL.Database(buffer);
5989
6453
  } else {
5990
6454
  db = new SQL.Database();
@@ -6001,10 +6465,10 @@ function saveDb(db) {
6001
6465
  if (!d) return;
6002
6466
  try {
6003
6467
  const kumaDir = getKumaDir();
6004
- const dbPath = path18.join(kumaDir, DB_FILENAME);
6468
+ const dbPath = path20.join(kumaDir, DB_FILENAME);
6005
6469
  const data = d.export();
6006
6470
  const buffer = Buffer.from(data);
6007
- fs18.writeFileSync(dbPath, buffer);
6471
+ fs19.writeFileSync(dbPath, buffer);
6008
6472
  } catch (err) {
6009
6473
  console.error(`[KumaDB] Failed to save database: ${err}`);
6010
6474
  }
@@ -6111,26 +6575,26 @@ function createSchema(db) {
6111
6575
  }
6112
6576
 
6113
6577
  // src/engine/kumaSelfHeal.ts
6114
- import { execSync as execSync7 } from "child_process";
6115
- import fs19 from "fs";
6116
- import path19 from "path";
6578
+ import { execSync as execSync9 } from "child_process";
6579
+ import fs20 from "fs";
6580
+ import path21 from "path";
6117
6581
  import crypto from "crypto";
6118
6582
  function contentFingerprint(filePath) {
6119
6583
  try {
6120
- const fullPath = path19.join(getProjectRoot(), filePath);
6121
- if (!fs19.existsSync(fullPath)) return null;
6122
- const stat = fs19.statSync(fullPath);
6584
+ const fullPath = path21.join(getProjectRoot(), filePath);
6585
+ if (!fs20.existsSync(fullPath)) return null;
6586
+ const stat = fs20.statSync(fullPath);
6123
6587
  if (!stat.isFile()) return null;
6124
- const fd = fs19.openSync(fullPath, "r");
6588
+ const fd = fs20.openSync(fullPath, "r");
6125
6589
  try {
6126
6590
  const size = stat.size;
6127
6591
  const readSize = Math.min(1024, size);
6128
6592
  const head = Buffer.alloc(readSize);
6129
- fs19.readSync(fd, head, 0, readSize, 0);
6593
+ fs20.readSync(fd, head, 0, readSize, 0);
6130
6594
  let tail = Buffer.alloc(0);
6131
6595
  if (size > 2048) {
6132
6596
  tail = Buffer.alloc(1024);
6133
- fs19.readSync(fd, tail, 0, 1024, size - 1024);
6597
+ fs20.readSync(fd, tail, 0, 1024, size - 1024);
6134
6598
  }
6135
6599
  const hash = crypto.createHash("md5");
6136
6600
  hash.update(head);
@@ -6138,7 +6602,7 @@ function contentFingerprint(filePath) {
6138
6602
  hash.update(String(size));
6139
6603
  return hash.digest("hex");
6140
6604
  } finally {
6141
- fs19.closeSync(fd);
6605
+ fs20.closeSync(fd);
6142
6606
  }
6143
6607
  } catch {
6144
6608
  return null;
@@ -6147,8 +6611,8 @@ function contentFingerprint(filePath) {
6147
6611
  function findByFingerprint(fingerprint, oldName) {
6148
6612
  try {
6149
6613
  const root = getProjectRoot();
6150
- const ext = path19.extname(oldName);
6151
- const result = execSync7(
6614
+ const ext = path21.extname(oldName);
6615
+ const result = execSync9(
6152
6616
  `find . -name "*${ext}" -type f -not -path "./node_modules/*" -not -path "./.git/*" -not -path "./dist/*" -not -path "./build/*" 2>/dev/null | head -200`,
6153
6617
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 1e4 }
6154
6618
  ).trim();
@@ -6181,8 +6645,8 @@ async function detectStaleNodes() {
6181
6645
  const row = stmt.getAsObject();
6182
6646
  const filePath = row.file_path;
6183
6647
  if (filePath.startsWith("search::") || filePath.startsWith("api_route::")) continue;
6184
- const fullPath = path19.join(root, filePath);
6185
- if (!fs19.existsSync(fullPath)) {
6648
+ const fullPath = path21.join(root, filePath);
6649
+ if (!fs20.existsSync(fullPath)) {
6186
6650
  const newPath = findRenamedPath(filePath);
6187
6651
  if (newPath) {
6188
6652
  stale.push({
@@ -6195,7 +6659,7 @@ async function detectStaleNodes() {
6195
6659
  });
6196
6660
  } else {
6197
6661
  const oldFingerprint = contentFingerprint(filePath);
6198
- const contentMatch = oldFingerprint ? findByFingerprint(oldFingerprint, path19.basename(filePath)) : null;
6662
+ const contentMatch = oldFingerprint ? findByFingerprint(oldFingerprint, path21.basename(filePath)) : null;
6199
6663
  stale.push({
6200
6664
  nodeId: row.id,
6201
6665
  type: row.type,
@@ -6216,7 +6680,7 @@ async function detectStaleNodes() {
6216
6680
  function findRenamedPath(oldPath) {
6217
6681
  try {
6218
6682
  const root = getProjectRoot();
6219
- const output = execSync7(
6683
+ const output = execSync9(
6220
6684
  `git log --follow --diff-filter=R --name-only --format="" -1 -- "${oldPath}"`,
6221
6685
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 5e3 }
6222
6686
  ).trim();
@@ -6297,8 +6761,8 @@ async function healOnQuery(filePaths) {
6297
6761
  try {
6298
6762
  const stalePaths = filePaths.filter((fp) => {
6299
6763
  if (!fp || fp.startsWith("search::") || fp.startsWith("api_route::")) return false;
6300
- const fullPath = path19.join(getProjectRoot(), fp);
6301
- return !fs19.existsSync(fullPath);
6764
+ const fullPath = path21.join(getProjectRoot(), fp);
6765
+ return !fs20.existsSync(fullPath);
6302
6766
  });
6303
6767
  if (stalePaths.length === 0) return { healed: 0 };
6304
6768
  let healed = 0;
@@ -6827,8 +7291,8 @@ async function pruneExperiences(keepPerTool = 50) {
6827
7291
 
6828
7292
  // src/engine/lspClient.ts
6829
7293
  import { spawn as spawn2 } from "child_process";
6830
- import path20 from "path";
6831
- import fs20 from "fs";
7294
+ import path22 from "path";
7295
+ import fs21 from "fs";
6832
7296
  var LSPClient = class {
6833
7297
  process = null;
6834
7298
  requestId = 0;
@@ -6866,8 +7330,8 @@ var LSPClient = class {
6866
7330
  console.error(`[LSP] typescript-language-server not found. LSP features will fallback to regex.`);
6867
7331
  return;
6868
7332
  }
6869
- const tsconfigPath = path20.join(root, "tsconfig.json");
6870
- if (!fs20.existsSync(tsconfigPath)) {
7333
+ const tsconfigPath = path22.join(root, "tsconfig.json");
7334
+ if (!fs21.existsSync(tsconfigPath)) {
6871
7335
  console.error(`[LSP] Warning: tsconfig.json not found at "${root}". Running in implicit project mode.`);
6872
7336
  }
6873
7337
  this.process = spawn2(lspBinary, ["--stdio", "--log-level=4"], {
@@ -6929,19 +7393,19 @@ var LSPClient = class {
6929
7393
  /** Resolve the typescript-language-server binary from local or global install */
6930
7394
  resolveLspBinary(projectRoot) {
6931
7395
  const candidates = [
6932
- path20.join(projectRoot, "node_modules", ".bin", "typescript-language-server"),
6933
- path20.join(projectRoot, "..", "node_modules", ".bin", "typescript-language-server")
7396
+ path22.join(projectRoot, "node_modules", ".bin", "typescript-language-server"),
7397
+ path22.join(projectRoot, "..", "node_modules", ".bin", "typescript-language-server")
6934
7398
  ];
6935
7399
  for (const candidate of candidates) {
6936
- if (fs20.existsSync(candidate)) {
7400
+ if (fs21.existsSync(candidate)) {
6937
7401
  return candidate;
6938
7402
  }
6939
7403
  }
6940
7404
  try {
6941
7405
  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)) {
7406
+ for (const dir of envPath.split(path22.delimiter)) {
7407
+ const binPath = path22.join(dir, "typescript-language-server");
7408
+ if (fs21.existsSync(binPath)) {
6945
7409
  return binPath;
6946
7410
  }
6947
7411
  }
@@ -6963,7 +7427,7 @@ var LSPClient = class {
6963
7427
  this.openDocuments.add(filePath);
6964
7428
  let content;
6965
7429
  try {
6966
- content = fs20.readFileSync(filePath, "utf-8");
7430
+ content = fs21.readFileSync(filePath, "utf-8");
6967
7431
  } catch {
6968
7432
  content = "";
6969
7433
  }
@@ -7180,8 +7644,8 @@ var LSPClient = class {
7180
7644
  var lspClient = new LSPClient();
7181
7645
 
7182
7646
  // src/tools/lspTools.ts
7183
- import fs21 from "fs";
7184
- import path21 from "path";
7647
+ import fs22 from "fs";
7648
+ import path23 from "path";
7185
7649
  async function handleFindReferences(params) {
7186
7650
  const { filePath, line, character } = params;
7187
7651
  const validation = validateFilePath(filePath);
@@ -7189,7 +7653,7 @@ async function handleFindReferences(params) {
7189
7653
  return `Error: ${validation.error.message}`;
7190
7654
  }
7191
7655
  const resolvedPath = validation.resolvedPath;
7192
- if (!fs21.existsSync(resolvedPath)) {
7656
+ if (!fs22.existsSync(resolvedPath)) {
7193
7657
  return `Error: File not found: "${filePath}".`;
7194
7658
  }
7195
7659
  if (!lspClient.isAvailable()) {
@@ -7214,7 +7678,7 @@ No references found for symbol at this position.`;
7214
7678
  const enrichedRefs = references.map((ref) => {
7215
7679
  let lineContent = "";
7216
7680
  try {
7217
- const content = fs21.readFileSync(ref.filePath, "utf-8");
7681
+ const content = fs22.readFileSync(ref.filePath, "utf-8");
7218
7682
  const lines2 = content.split("\n");
7219
7683
  lineContent = lines2[ref.line]?.trim() ?? "";
7220
7684
  } catch {
@@ -7230,11 +7694,11 @@ No references found for symbol at this position.`;
7230
7694
  const projectRoot = getProjectRoot();
7231
7695
  const lines = [
7232
7696
  `**Find References** \u2014 ${enrichedRefs.length} references found`,
7233
- `\u{1F4CD} File: ${path21.relative(projectRoot, resolvedPath)}:${line + 1}:${character + 1}`,
7697
+ `\u{1F4CD} File: ${path23.relative(projectRoot, resolvedPath)}:${line + 1}:${character + 1}`,
7234
7698
  ""
7235
7699
  ];
7236
7700
  for (const [file, refs] of grouped) {
7237
- const relPath = path21.relative(projectRoot, file);
7701
+ const relPath = path23.relative(projectRoot, file);
7238
7702
  lines.push(`**\u{1F4C4} ${relPath}:**`);
7239
7703
  for (const ref of refs) {
7240
7704
  const loc = `L${ref.line + 1}:${ref.character + 1}`;
@@ -7255,7 +7719,7 @@ async function handleGoToDefinition(params) {
7255
7719
  return `Error: ${validation.error.message}`;
7256
7720
  }
7257
7721
  const resolvedPath = validation.resolvedPath;
7258
- if (!fs21.existsSync(resolvedPath)) {
7722
+ if (!fs22.existsSync(resolvedPath)) {
7259
7723
  return `Error: File not found: "${filePath}".`;
7260
7724
  }
7261
7725
  if (!lspClient.isAvailable()) {
@@ -7278,10 +7742,10 @@ Try selecting a smaller/cleaner position, or install typescript-language-server
7278
7742
  Cannot find definition for symbol at this position.`;
7279
7743
  }
7280
7744
  const projectRoot = getProjectRoot();
7281
- const relPath = path21.relative(projectRoot, definition.filePath);
7745
+ const relPath = path23.relative(projectRoot, definition.filePath);
7282
7746
  let lineContent = "";
7283
7747
  try {
7284
- const content = fs21.readFileSync(definition.filePath, "utf-8");
7748
+ const content = fs22.readFileSync(definition.filePath, "utf-8");
7285
7749
  const lines2 = content.split("\n");
7286
7750
  lineContent = lines2[definition.line]?.trim() ?? "";
7287
7751
  } catch {
@@ -7313,7 +7777,7 @@ async function handleRenameSymbol(params) {
7313
7777
  return `Error: ${validation.error.message}`;
7314
7778
  }
7315
7779
  const resolvedPath = validation.resolvedPath;
7316
- if (!fs21.existsSync(resolvedPath)) {
7780
+ if (!fs22.existsSync(resolvedPath)) {
7317
7781
  return `Error: File not found: "${filePath}".`;
7318
7782
  }
7319
7783
  if (!lspClient.isAvailable()) {
@@ -7344,7 +7808,7 @@ Make sure:
7344
7808
  const fileChanges = [];
7345
7809
  for (const change of result.changes) {
7346
7810
  try {
7347
- const content = fs21.readFileSync(change.filePath, "utf-8");
7811
+ const content = fs22.readFileSync(change.filePath, "utf-8");
7348
7812
  const lines2 = content.split("\n");
7349
7813
  const sortedEdits = [...change.edits].sort((a, b) => {
7350
7814
  if (b.line !== a.line) return b.line - a.line;
@@ -7358,10 +7822,10 @@ Make sure:
7358
7822
  lines2[edit.line] = before + edit.newText + after;
7359
7823
  }
7360
7824
  }
7361
- fs21.writeFileSync(change.filePath, lines2.join("\n"), "utf-8");
7825
+ fs22.writeFileSync(change.filePath, lines2.join("\n"), "utf-8");
7362
7826
  totalEdits += change.edits.length;
7363
7827
  fileChanges.push({
7364
- filePath: path21.relative(projectRoot, change.filePath),
7828
+ filePath: path23.relative(projectRoot, change.filePath),
7365
7829
  editCount: change.edits.length
7366
7830
  });
7367
7831
  } catch (err) {
@@ -7388,14 +7852,14 @@ async function handleGetTypeInfo(params) {
7388
7852
  return `Error: ${validation.error.message}`;
7389
7853
  }
7390
7854
  const resolvedPath = validation.resolvedPath;
7391
- if (!fs21.existsSync(resolvedPath)) {
7855
+ if (!fs22.existsSync(resolvedPath)) {
7392
7856
  return `Error: File not found: "${filePath}".`;
7393
7857
  }
7394
7858
  if (!lspClient.isAvailable()) {
7395
7859
  sessionMemory.recordToolCall("lsp_query", { action: "type", filePath, line, character, fallback: "line-context" });
7396
7860
  console.error(`[LSP] typescript-language-server not found. Using file-read fallback for type info.`);
7397
7861
  try {
7398
- const fileContent = fs21.readFileSync(resolvedPath, "utf-8");
7862
+ const fileContent = fs22.readFileSync(resolvedPath, "utf-8");
7399
7863
  const fileLines = fileContent.split("\n");
7400
7864
  const targetLine = fileLines[line];
7401
7865
  if (!targetLine) {
@@ -7403,7 +7867,7 @@ async function handleGetTypeInfo(params) {
7403
7867
  }
7404
7868
  const symbolName = extractSymbolAtPosition(resolvedPath, line, character);
7405
7869
  const projectRoot = getProjectRoot();
7406
- const relPath = path21.relative(projectRoot, resolvedPath);
7870
+ const relPath = path23.relative(projectRoot, resolvedPath);
7407
7871
  const contextStart = Math.max(0, line - 3);
7408
7872
  const contextEnd = Math.min(fileLines.length, line + 4);
7409
7873
  const contextLines = [];
@@ -7438,7 +7902,7 @@ async function handleGetTypeInfo(params) {
7438
7902
  No type info for this position.`;
7439
7903
  }
7440
7904
  const projectRoot = getProjectRoot();
7441
- const relPath = path21.relative(projectRoot, resolvedPath);
7905
+ const relPath = path23.relative(projectRoot, resolvedPath);
7442
7906
  const lines = [
7443
7907
  `\u{1F4CB} **Type Info** \u2014 \`${relPath}:${line + 1}:${character + 1}\``,
7444
7908
  "",
@@ -7479,7 +7943,7 @@ async function handleLspQuery(params) {
7479
7943
  }
7480
7944
  function extractSymbolAtPosition(filePath, line, character) {
7481
7945
  try {
7482
- const content = fs21.readFileSync(filePath, "utf-8");
7946
+ const content = fs22.readFileSync(filePath, "utf-8");
7483
7947
  const lines = content.split("\n");
7484
7948
  const targetLine = lines[line];
7485
7949
  if (!targetLine) return null;
@@ -7497,9 +7961,9 @@ function extractSymbolAtPosition(filePath, line, character) {
7497
7961
  }
7498
7962
  async function fallbackGrepReferences(symbolName, _filePath, _line, _character) {
7499
7963
  try {
7500
- const { default: fg4 } = await import("fast-glob");
7964
+ const { default: fg6 } = await import("fast-glob");
7501
7965
  const root = getProjectRoot();
7502
- const tsFiles = await fg4(["**/*.{ts,tsx,js,jsx}"], {
7966
+ const tsFiles = await fg6(["**/*.{ts,tsx,js,jsx}"], {
7503
7967
  cwd: root,
7504
7968
  ignore: ["node_modules/**", "dist/**", ".git/**"],
7505
7969
  onlyFiles: true,
@@ -7510,7 +7974,7 @@ async function fallbackGrepReferences(symbolName, _filePath, _line, _character)
7510
7974
  const regex = new RegExp(`\\b${escapedSymbol}\\b`, "g");
7511
7975
  for (const file of tsFiles.slice(0, 100)) {
7512
7976
  try {
7513
- const content = fs21.readFileSync(file, "utf-8");
7977
+ const content = fs22.readFileSync(file, "utf-8");
7514
7978
  const lines2 = content.split("\n");
7515
7979
  for (let i = 0; i < lines2.length; i++) {
7516
7980
  if (regex.test(lines2[i])) {
@@ -7539,7 +8003,7 @@ No references found. Symbol may not be used in other files.`;
7539
8003
  ""
7540
8004
  ];
7541
8005
  if (grouped.size > 1) {
7542
- const fileList = [...grouped.keys()].map((f) => path21.relative(projectRoot, f)).join(", ");
8006
+ const fileList = [...grouped.keys()].map((f) => path23.relative(projectRoot, f)).join(", ");
7543
8007
  lines.push(`**Ambiguity note:** Found matches across ${grouped.size} different files (${fileList}).`);
7544
8008
  lines.push(` Verify each match is the right scope \u2014 results may include same-named symbols.`);
7545
8009
  lines.push("");
@@ -7556,7 +8020,7 @@ No references found. Symbol may not be used in other files.`;
7556
8020
  }
7557
8021
  }
7558
8022
  for (const [file, refs] of grouped) {
7559
- const relPath = path21.relative(projectRoot, file);
8023
+ const relPath = path23.relative(projectRoot, file);
7560
8024
  lines.push(`**\u{1F4C4} ${relPath}:**`);
7561
8025
  for (const ref of refs) {
7562
8026
  lines.push(` \u2514 L${ref.line} \u2014 ${ref.content}`);
@@ -7571,9 +8035,9 @@ No references found. Symbol may not be used in other files.`;
7571
8035
  }
7572
8036
  async function fallbackGrepDefinition(symbolName) {
7573
8037
  try {
7574
- const { default: fg4 } = await import("fast-glob");
8038
+ const { default: fg6 } = await import("fast-glob");
7575
8039
  const root = getProjectRoot();
7576
- const tsFiles = await fg4(["**/*.{ts,tsx,js,jsx}"], {
8040
+ const tsFiles = await fg6(["**/*.{ts,tsx,js,jsx}"], {
7577
8041
  cwd: root,
7578
8042
  ignore: ["node_modules/**", "dist/**", ".git/**"],
7579
8043
  onlyFiles: true,
@@ -7590,14 +8054,14 @@ async function fallbackGrepDefinition(symbolName) {
7590
8054
  ];
7591
8055
  for (const file of tsFiles) {
7592
8056
  try {
7593
- const content = fs21.readFileSync(file, "utf-8");
8057
+ const content = fs22.readFileSync(file, "utf-8");
7594
8058
  const lines = content.split("\n");
7595
8059
  for (let i = 0; i < lines.length; i++) {
7596
8060
  const trimmed = lines[i].trim();
7597
8061
  for (const pattern of declPatterns) {
7598
8062
  if (pattern.test(trimmed)) {
7599
8063
  const projectRoot = getProjectRoot();
7600
- const relPath = path21.relative(projectRoot, file);
8064
+ const relPath = path23.relative(projectRoot, file);
7601
8065
  return [
7602
8066
  `\u{1F4CD} **Go to Definition**`,
7603
8067
  `\u{1F4C4} File: \`${relPath}\``,
@@ -7620,11 +8084,11 @@ Cannot find definition.`;
7620
8084
  }
7621
8085
 
7622
8086
  // src/engine/kumaTimeMachine.ts
7623
- import { execSync as execSync8 } from "child_process";
8087
+ import { execSync as execSync10 } from "child_process";
7624
8088
  function getBlameForFile(filePath) {
7625
8089
  const root = getProjectRoot();
7626
8090
  try {
7627
- const output = execSync8(
8091
+ const output = execSync10(
7628
8092
  `git blame --line-porcelain -- "${filePath}"`,
7629
8093
  { cwd: root, encoding: "utf-8", maxBuffer: 2 * 1024 * 1024, timeout: 1e4 }
7630
8094
  );
@@ -7670,7 +8134,7 @@ function getBlameForFile(filePath) {
7670
8134
  function getFileHistory(filePath, maxCount = 20) {
7671
8135
  const root = getProjectRoot();
7672
8136
  try {
7673
- const output = execSync8(
8137
+ const output = execSync10(
7674
8138
  `git log --follow --format="%H||%an||%ai||%s" --shortstat -- "${filePath}"`,
7675
8139
  { cwd: root, encoding: "utf-8", maxBuffer: 2 * 1024 * 1024, timeout: 1e4 }
7676
8140
  );
@@ -7705,7 +8169,7 @@ async function getSymbolTimeline(symbolName, filePath, symbolType = "function")
7705
8169
  const blame = getBlameForFile(filePath);
7706
8170
  const pattern = getSymbolPattern(symbolName, symbolType);
7707
8171
  const root = getProjectRoot();
7708
- const content = execSync8(`git show HEAD:"${filePath}"`, {
8172
+ const content = execSync10(`git show HEAD:"${filePath}"`, {
7709
8173
  cwd: root,
7710
8174
  encoding: "utf-8",
7711
8175
  maxBuffer: 2 * 1024 * 1024,
@@ -7732,7 +8196,7 @@ async function getSymbolTimeline(symbolName, filePath, symbolType = "function")
7732
8196
  const entries = [];
7733
8197
  for (const [hash, data] of commitMap) {
7734
8198
  try {
7735
- const msg = execSync8(
8199
+ const msg = execSync10(
7736
8200
  `git log -1 --format="%s" ${hash}`,
7737
8201
  { cwd: root, encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 3e3 }
7738
8202
  ).trim();
@@ -7917,10 +8381,10 @@ async function getIntentPatterns(limit = 10) {
7917
8381
  ORDER BY id ASC
7918
8382
  `);
7919
8383
  pathStmt.bind([session.id]);
7920
- const path29 = [];
8384
+ const path31 = [];
7921
8385
  while (pathStmt.step()) {
7922
8386
  const row = pathStmt.getAsObject();
7923
- path29.push(row.tool_name);
8387
+ path31.push(row.tool_name);
7924
8388
  }
7925
8389
  pathStmt.free();
7926
8390
  const existing = clusterMap.get(clusterKey) || {
@@ -7929,7 +8393,7 @@ async function getIntentPatterns(limit = 10) {
7929
8393
  totalDuration: 0,
7930
8394
  lastUsed: 0
7931
8395
  };
7932
- existing.paths.push(path29);
8396
+ existing.paths.push(path31);
7933
8397
  existing.successes.push(session.success);
7934
8398
  existing.lastUsed = Math.max(existing.lastUsed, session.startedAt);
7935
8399
  clusterMap.set(clusterKey, existing);
@@ -8037,9 +8501,9 @@ function formatIntentSuggestion(intent, suggestion) {
8037
8501
  }
8038
8502
 
8039
8503
  // src/engine/kumaArchGuard.ts
8040
- import fg3 from "fast-glob";
8041
- import fs22 from "fs";
8042
- import path22 from "path";
8504
+ import fg5 from "fast-glob";
8505
+ import fs23 from "fs";
8506
+ import path24 from "path";
8043
8507
  var ARCHITECTURES = [
8044
8508
  {
8045
8509
  name: "clean-architecture",
@@ -8130,8 +8594,8 @@ var ARCHITECTURES = [
8130
8594
  ];
8131
8595
  function detectArchitecture() {
8132
8596
  const root = getProjectRoot();
8133
- const srcDir = path22.join(root, "src");
8134
- if (!fs22.existsSync(srcDir)) {
8597
+ const srcDir = path24.join(root, "src");
8598
+ if (!fs23.existsSync(srcDir)) {
8135
8599
  return getArchitectureProfile("flat");
8136
8600
  }
8137
8601
  const scores = ARCHITECTURES.map((arch) => {
@@ -8142,7 +8606,7 @@ function detectArchitecture() {
8142
8606
  totalPatterns++;
8143
8607
  const globPattern = pattern.replace(/\/\*\*$/, "/**");
8144
8608
  try {
8145
- const matches = fg3.sync(globPattern, { cwd: root, onlyFiles: false, deep: 1 });
8609
+ const matches = fg5.sync(globPattern, { cwd: root, onlyFiles: false, deep: 1 });
8146
8610
  if (matches.length > 0) matchCount++;
8147
8611
  } catch {
8148
8612
  }
@@ -8153,10 +8617,10 @@ function detectArchitecture() {
8153
8617
  scores.sort((a, b) => b.score - a.score);
8154
8618
  const best = scores[0];
8155
8619
  if (!best || best.score < 0.2) {
8156
- if (fs22.existsSync(path22.join(srcDir, "domain")) || fs22.existsSync(path22.join(srcDir, "entities"))) {
8620
+ if (fs23.existsSync(path24.join(srcDir, "domain")) || fs23.existsSync(path24.join(srcDir, "entities"))) {
8157
8621
  return getArchitectureProfile("clean-architecture");
8158
8622
  }
8159
- if (fs22.existsSync(path22.join(srcDir, "data")) || fs22.existsSync(path22.join(srcDir, "business"))) {
8623
+ if (fs23.existsSync(path24.join(srcDir, "data")) || fs23.existsSync(path24.join(srcDir, "business"))) {
8160
8624
  return getArchitectureProfile("layered-architecture");
8161
8625
  }
8162
8626
  return getArchitectureProfile("flat");
@@ -8227,7 +8691,7 @@ async function scanFilesystemForViolations(profile) {
8227
8691
  const arch = profile || detectArchitecture();
8228
8692
  const root = getProjectRoot();
8229
8693
  try {
8230
- const files = await fg3(["src/**/*.{ts,tsx,js,jsx}"], {
8694
+ const files = await fg5(["src/**/*.{ts,tsx,js,jsx}"], {
8231
8695
  cwd: root,
8232
8696
  ignore: ["**/node_modules/**", "**/*.test.*", "**/*.spec.*", "**/*.d.ts"],
8233
8697
  onlyFiles: true
@@ -8236,7 +8700,7 @@ async function scanFilesystemForViolations(profile) {
8236
8700
  const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
8237
8701
  for (const file of files.slice(0, 200)) {
8238
8702
  try {
8239
- const content = fs22.readFileSync(path22.join(root, file), "utf-8");
8703
+ const content = fs23.readFileSync(path24.join(root, file), "utf-8");
8240
8704
  const imports = [];
8241
8705
  let match;
8242
8706
  while ((match = importRegex.exec(content)) !== null) {
@@ -8249,16 +8713,16 @@ async function scanFilesystemForViolations(profile) {
8249
8713
  if (!imp.startsWith(".") && !imp.startsWith("/") && !imp.startsWith("src/")) continue;
8250
8714
  let targetFile = imp;
8251
8715
  if (imp.startsWith(".")) {
8252
- const resolved = path22.resolve(path22.dirname(file), imp);
8253
- targetFile = path22.relative(root, resolved);
8716
+ const resolved = path24.resolve(path24.dirname(file), imp);
8717
+ targetFile = path24.relative(root, resolved);
8254
8718
  } else if (imp.startsWith("/")) {
8255
8719
  targetFile = imp.substring(1);
8256
8720
  }
8257
- if (!path22.extname(targetFile)) {
8721
+ if (!path24.extname(targetFile)) {
8258
8722
  const extVariants = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js"];
8259
8723
  let found = false;
8260
8724
  for (const ext of extVariants) {
8261
- if (fs22.existsSync(path22.join(root, targetFile + ext))) {
8725
+ if (fs23.existsSync(path24.join(root, targetFile + ext))) {
8262
8726
  targetFile += ext;
8263
8727
  found = true;
8264
8728
  break;
@@ -8342,20 +8806,20 @@ function formatArchitectureDetection(profile) {
8342
8806
  }
8343
8807
 
8344
8808
  // src/engine/kumaMemory.ts
8345
- import fs23 from "fs";
8346
- import path23 from "path";
8809
+ import fs24 from "fs";
8810
+ import path25 from "path";
8347
8811
  var MEMORY_DIR = ".kuma/memories";
8348
8812
  function scoreMemoryRelevance(context, limit = 5) {
8349
8813
  const results = [];
8350
- const kumaDir = path23.join(getProjectRoot(), MEMORY_DIR);
8351
- if (!fs23.existsSync(kumaDir)) return [];
8814
+ const kumaDir = path25.join(getProjectRoot(), MEMORY_DIR);
8815
+ if (!fs24.existsSync(kumaDir)) return [];
8352
8816
  const terms = context.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
8353
8817
  if (terms.length === 0) return [];
8354
8818
  try {
8355
- const files = fs23.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
8819
+ const files = fs24.readdirSync(kumaDir).filter((f) => f.endsWith(".md"));
8356
8820
  for (const file of files) {
8357
8821
  try {
8358
- const content = fs23.readFileSync(path23.join(kumaDir, file), "utf-8");
8822
+ const content = fs24.readFileSync(path25.join(kumaDir, file), "utf-8");
8359
8823
  const lower = content.toLowerCase();
8360
8824
  let matchCount = 0;
8361
8825
  for (const term of terms) {
@@ -8435,7 +8899,7 @@ function formatDecisionTemplate() {
8435
8899
  }
8436
8900
 
8437
8901
  // src/engine/kumaContextEngine.ts
8438
- import { execSync as execSync9 } from "child_process";
8902
+ import { execSync as execSync11 } from "child_process";
8439
8903
  var STOP_WORDS = /* @__PURE__ */ new Set([
8440
8904
  "the",
8441
8905
  "this",
@@ -8552,7 +9016,7 @@ async function searchNodesFts(db, terms) {
8552
9016
  function getFileRecencySeconds(filePath) {
8553
9017
  try {
8554
9018
  const escapedPath = filePath.replace(/"/g, '\\"');
8555
- const result = execSync9(
9019
+ const result = execSync11(
8556
9020
  'git log -1 --format=%ct -- "' + escapedPath + '" 2>/dev/null || echo 0',
8557
9021
  { encoding: "utf-8", timeout: 2e3 }
8558
9022
  );
@@ -9269,9 +9733,9 @@ async function buildOwnershipMap(db, focus, limit) {
9269
9733
  const dirGroups = /* @__PURE__ */ new Map();
9270
9734
  while (stmt.step()) {
9271
9735
  const row = stmt.getAsObject();
9272
- const path29 = row.file_path || row.name;
9273
- const dir = topDir(path29);
9274
- if (focus && !path29.includes(focus)) continue;
9736
+ const path31 = row.file_path || row.name;
9737
+ const dir = topDir(path31);
9738
+ if (focus && !path31.includes(focus)) continue;
9275
9739
  if (!dirGroups.has(dir)) dirGroups.set(dir, []);
9276
9740
  const existing = dirGroups.get(dir);
9277
9741
  const emoji = row.type === "function" ? "\u{1F527}" : row.type === "file" ? "\u{1F4C4}" : "\u{1F4CC}";
@@ -9312,8 +9776,8 @@ function mermaidId(name) {
9312
9776
  }
9313
9777
 
9314
9778
  // src/engine/kumaHeatMap.ts
9315
- import fs24 from "fs";
9316
- import path24 from "path";
9779
+ import fs25 from "fs";
9780
+ import path26 from "path";
9317
9781
  async function computeHeatMap() {
9318
9782
  const history = sessionMemory.getToolCallHistory(100);
9319
9783
  const db = await getDb();
@@ -9322,7 +9786,7 @@ async function computeHeatMap() {
9322
9786
  const p = call.params;
9323
9787
  const fp = p.filePath || p.file || "";
9324
9788
  if (!fp) continue;
9325
- const dir = path24.dirname(fp).split("/")[0] || ".";
9789
+ const dir = path26.dirname(fp).split("/")[0] || ".";
9326
9790
  const entry = dirData.get(dir) || { edits: 0, reads: 0, failures: 0 };
9327
9791
  if (call.toolName === "precise_diff_editor" || call.toolName === "batch_file_writer") {
9328
9792
  entry.edits++;
@@ -9350,12 +9814,12 @@ async function computeHeatMap() {
9350
9814
  } catch {
9351
9815
  }
9352
9816
  const root = getProjectRoot();
9353
- const srcDir = path24.join(root, "src");
9354
- if (fs24.existsSync(srcDir)) {
9817
+ const srcDir = path26.join(root, "src");
9818
+ if (fs25.existsSync(srcDir)) {
9355
9819
  try {
9356
- for (const entry of fs24.readdirSync(srcDir, { withFileTypes: true })) {
9820
+ for (const entry of fs25.readdirSync(srcDir, { withFileTypes: true })) {
9357
9821
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
9358
- const count = countFiles(path24.join(srcDir, entry.name));
9822
+ const count = countFiles(path26.join(srcDir, entry.name));
9359
9823
  if (!dirFileCounts.has(entry.name) || dirFileCounts.get(entry.name) < count) {
9360
9824
  dirFileCounts.set(entry.name, count);
9361
9825
  }
@@ -9465,9 +9929,9 @@ function formatHeatMap(report) {
9465
9929
  function countFiles(dir) {
9466
9930
  let count = 0;
9467
9931
  try {
9468
- for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
9932
+ for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
9469
9933
  if (entry.name.startsWith(".")) continue;
9470
- const full = path24.join(dir, entry.name);
9934
+ const full = path26.join(dir, entry.name);
9471
9935
  if (entry.isDirectory()) {
9472
9936
  count += countFiles(full);
9473
9937
  } else if (/\.(ts|tsx|js|jsx|go|rs|py|java|kt)$/.test(entry.name)) {
@@ -9767,72 +10231,72 @@ function simpleHash(str) {
9767
10231
  }
9768
10232
 
9769
10233
  // src/engine/kumaLock.ts
9770
- import fs25 from "fs";
9771
- import path25 from "path";
10234
+ import fs26 from "fs";
10235
+ import path27 from "path";
9772
10236
  var LOCKS_DIR = ".kuma/locks";
9773
10237
  function locksDir() {
9774
- return path25.join(getProjectRoot(), LOCKS_DIR);
10238
+ return path27.join(getProjectRoot(), LOCKS_DIR);
9775
10239
  }
9776
10240
  function ensureLocksDir() {
9777
10241
  const dir = locksDir();
9778
- if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
10242
+ if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
9779
10243
  }
9780
10244
  function lockPath(filePath) {
9781
10245
  const safeName = filePath.replace(/[^a-zA-Z0-9_./-]/g, "_");
9782
- return path25.join(locksDir(), `${safeName}.lock.json`);
10246
+ return path27.join(locksDir(), `${safeName}.lock.json`);
9783
10247
  }
9784
10248
  function acquireLock(filePath, agentId) {
9785
10249
  ensureLocksDir();
9786
10250
  const lp = lockPath(filePath);
9787
10251
  const id = agentId || `agent-${process.pid}`;
9788
- if (fs25.existsSync(lp)) {
10252
+ if (fs26.existsSync(lp)) {
9789
10253
  try {
9790
- const existing = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10254
+ const existing = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9791
10255
  if (existing.agentId === id) {
9792
10256
  return `\u{1F512} **Already locked** by you (${id}) on ${new Date(existing.acquiredAt).toISOString()}`;
9793
10257
  }
9794
10258
  const elapsed = Math.floor((Date.now() - existing.acquiredAt) / 1e3);
9795
10259
  if (elapsed > 300) {
9796
- fs25.unlinkSync(lp);
10260
+ fs26.unlinkSync(lp);
9797
10261
  } else {
9798
10262
  return `\u{1F512} **Locked** by ${existing.agentId} since ${new Date(existing.acquiredAt).toISOString()} (${elapsed}s ago)`;
9799
10263
  }
9800
10264
  } catch {
9801
- fs25.unlinkSync(lp);
10265
+ fs26.unlinkSync(lp);
9802
10266
  }
9803
10267
  }
9804
10268
  const entry = { filePath, agentId: id, acquiredAt: Date.now(), status: "locked" };
9805
- fs25.writeFileSync(lp, JSON.stringify(entry, null, 2), "utf-8");
10269
+ fs26.writeFileSync(lp, JSON.stringify(entry, null, 2), "utf-8");
9806
10270
  return `\u{1F513} **Lock acquired** on \`${filePath}\` by ${id}`;
9807
10271
  }
9808
10272
  function releaseLock(filePath, agentId) {
9809
10273
  ensureLocksDir();
9810
10274
  const lp = lockPath(filePath);
9811
10275
  const id = agentId || `agent-${process.pid}`;
9812
- if (!fs25.existsSync(lp)) {
10276
+ if (!fs26.existsSync(lp)) {
9813
10277
  return `\u26A0\uFE0F No lock found for \`${filePath}\``;
9814
10278
  }
9815
10279
  try {
9816
- const existing = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10280
+ const existing = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9817
10281
  if (existing.agentId !== id) {
9818
10282
  return `\u26A0\uFE0F Cannot release lock held by ${existing.agentId}. Use force:true to override.`;
9819
10283
  }
9820
- fs25.unlinkSync(lp);
10284
+ fs26.unlinkSync(lp);
9821
10285
  return `\u{1F513} **Lock released** on \`${filePath}\``;
9822
10286
  } catch {
9823
- fs25.unlinkSync(lp);
10287
+ fs26.unlinkSync(lp);
9824
10288
  return `\u{1F513} **Lock released** (force cleanup) on \`${filePath}\``;
9825
10289
  }
9826
10290
  }
9827
10291
  function listLocks() {
9828
10292
  ensureLocksDir();
9829
10293
  const dir = locksDir();
9830
- const files = fs25.readdirSync(dir).filter((f) => f.endsWith(".lock.json"));
10294
+ const files = fs26.readdirSync(dir).filter((f) => f.endsWith(".lock.json"));
9831
10295
  if (files.length === 0) return "\u{1F513} No active locks.";
9832
10296
  const lines = ["\u{1F512} **Active Locks:**", ""];
9833
10297
  for (const f of files) {
9834
10298
  try {
9835
- const entry = JSON.parse(fs25.readFileSync(path25.join(dir, f), "utf-8"));
10299
+ const entry = JSON.parse(fs26.readFileSync(path27.join(dir, f), "utf-8"));
9836
10300
  const elapsed = Math.floor((Date.now() - entry.acquiredAt) / 1e3);
9837
10301
  lines.push(` \u2022 \`${entry.filePath}\` \u2014 locked by ${entry.agentId} (${elapsed}s ago)`);
9838
10302
  } catch {
@@ -9843,9 +10307,9 @@ function listLocks() {
9843
10307
  }
9844
10308
  function isLocked(filePath) {
9845
10309
  const lp = lockPath(filePath);
9846
- if (!fs25.existsSync(lp)) return { locked: false };
10310
+ if (!fs26.existsSync(lp)) return { locked: false };
9847
10311
  try {
9848
- const entry = JSON.parse(fs25.readFileSync(lp, "utf-8"));
10312
+ const entry = JSON.parse(fs26.readFileSync(lp, "utf-8"));
9849
10313
  return { locked: true, by: entry.agentId, since: entry.acquiredAt };
9850
10314
  } catch {
9851
10315
  return { locked: false };
@@ -9855,16 +10319,16 @@ function cleanStaleLocks() {
9855
10319
  ensureLocksDir();
9856
10320
  const dir = locksDir();
9857
10321
  let count = 0;
9858
- for (const f of fs25.readdirSync(dir).filter((f2) => f2.endsWith(".lock.json"))) {
10322
+ for (const f of fs26.readdirSync(dir).filter((f2) => f2.endsWith(".lock.json"))) {
9859
10323
  try {
9860
- const entry = JSON.parse(fs25.readFileSync(path25.join(dir, f), "utf-8"));
10324
+ const entry = JSON.parse(fs26.readFileSync(path27.join(dir, f), "utf-8"));
9861
10325
  if (Date.now() - entry.acquiredAt > 3e5) {
9862
- fs25.unlinkSync(path25.join(dir, f));
10326
+ fs26.unlinkSync(path27.join(dir, f));
9863
10327
  count++;
9864
10328
  }
9865
10329
  } catch {
9866
10330
  try {
9867
- fs25.unlinkSync(path25.join(dir, f));
10331
+ fs26.unlinkSync(path27.join(dir, f));
9868
10332
  count++;
9869
10333
  } catch {
9870
10334
  }
@@ -10212,13 +10676,13 @@ function formatConfidence(report, target) {
10212
10676
  }
10213
10677
 
10214
10678
  // src/engine/kumaDNA.ts
10215
- import path26 from "path";
10679
+ import path28 from "path";
10216
10680
  async function generateDNA() {
10217
10681
  try {
10218
10682
  const db = await getDb();
10219
10683
  const summary = sessionMemory.getSummary();
10220
10684
  const root = getProjectRoot();
10221
- const projectName = path26.basename(root);
10685
+ const projectName = path28.basename(root);
10222
10686
  let totalFiles = 0, totalFunctions = 0, totalTests = 0;
10223
10687
  try {
10224
10688
  const filesR = db.exec("SELECT COUNT(*) as c FROM nodes WHERE type = 'file'");
@@ -11067,14 +11531,14 @@ async function handleSafetyCheck(params) {
11067
11531
  }
11068
11532
 
11069
11533
  // src/engine/kumaCollective.ts
11070
- import fs26 from "fs";
11071
- import path27 from "path";
11534
+ import fs27 from "fs";
11535
+ import path29 from "path";
11072
11536
  var DEFAULT_ENDPOINT = process.env.KUMA_COLLECTIVE_URL || "";
11073
11537
  function getSyncConfig() {
11074
11538
  try {
11075
- const configPath = path27.join(getProjectRoot(), ".kuma", "config.json");
11076
- if (fs26.existsSync(configPath)) {
11077
- const raw = fs26.readFileSync(configPath, "utf-8");
11539
+ const configPath = path29.join(getProjectRoot(), ".kuma", "config.json");
11540
+ if (fs27.existsSync(configPath)) {
11541
+ const raw = fs27.readFileSync(configPath, "utf-8");
11078
11542
  const config = JSON.parse(raw);
11079
11543
  if (config.collective) {
11080
11544
  return {
@@ -11094,12 +11558,12 @@ function getSyncConfig() {
11094
11558
  }
11095
11559
  function getInstanceId() {
11096
11560
  try {
11097
- const idPath = path27.join(getProjectRoot(), ".kuma", ".instance-id");
11098
- if (fs26.existsSync(idPath)) {
11099
- return fs26.readFileSync(idPath, "utf-8").trim();
11561
+ const idPath = path29.join(getProjectRoot(), ".kuma", ".instance-id");
11562
+ if (fs27.existsSync(idPath)) {
11563
+ return fs27.readFileSync(idPath, "utf-8").trim();
11100
11564
  }
11101
11565
  const id = `anon-${Math.random().toString(36).slice(2, 10)}`;
11102
- fs26.writeFileSync(idPath, id, "utf-8");
11566
+ fs27.writeFileSync(idPath, id, "utf-8");
11103
11567
  return id;
11104
11568
  } catch {
11105
11569
  return "anon-unknown";
@@ -11206,13 +11670,13 @@ function exportAnonymizedPatterns() {
11206
11670
  function detectProjectLanguage() {
11207
11671
  try {
11208
11672
  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";
11673
+ if (fs27.existsSync(path29.join(root, "go.mod"))) return "go";
11674
+ if (fs27.existsSync(path29.join(root, "Cargo.toml"))) return "rust";
11675
+ if (fs27.existsSync(path29.join(root, "composer.json"))) return "php";
11676
+ if (fs27.existsSync(path29.join(root, "pyproject.toml")) || fs27.existsSync(path29.join(root, "requirements.txt"))) return "python";
11677
+ if (fs27.existsSync(path29.join(root, "Gemfile"))) return "ruby";
11678
+ if (fs27.existsSync(path29.join(root, "pom.xml")) || fs27.existsSync(path29.join(root, "build.gradle"))) return "java";
11679
+ if (fs27.existsSync(path29.join(root, "package.json"))) return "typescript";
11216
11680
  return "unknown";
11217
11681
  } catch {
11218
11682
  return "unknown";
@@ -11368,9 +11832,9 @@ function formatCollectivePatterns(patterns) {
11368
11832
  }
11369
11833
 
11370
11834
  // src/engine/kumaMarketplace.ts
11371
- import { execSync as execSync10 } from "child_process";
11372
- import fs27 from "fs";
11373
- import path28 from "path";
11835
+ import { execSync as execSync12 } from "child_process";
11836
+ import fs28 from "fs";
11837
+ import path30 from "path";
11374
11838
  var BUILT_IN_TEMPLATES = [
11375
11839
  // ── Framework Web (JS/TS) ──
11376
11840
  {
@@ -11585,7 +12049,7 @@ async function listMarketplace() {
11585
12049
  `\u{1F4E6} ${templates.length} template(s) available`,
11586
12050
  ""
11587
12051
  ];
11588
- const LANG_MAP = [
12052
+ const LANG_MAP2 = [
11589
12053
  { lang: "typescript", keywords: ["typescript", "hono", "elysia", "tanstack", "zustand", "prisma", "drizzle", "shadcn"] },
11590
12054
  { lang: "javascript", keywords: ["javascript", "express"] },
11591
12055
  { lang: "go", keywords: ["go", "gin"] },
@@ -11605,7 +12069,7 @@ async function listMarketplace() {
11605
12069
  general: "\u{1F4E6}"
11606
12070
  };
11607
12071
  function detectLang(t) {
11608
- for (const entry of LANG_MAP) {
12072
+ for (const entry of LANG_MAP2) {
11609
12073
  if (entry.keywords.some((k) => t.tags.some((tag) => tag.includes(k)))) {
11610
12074
  return entry.lang;
11611
12075
  }
@@ -11650,17 +12114,17 @@ function scanNpmTemplates() {
11650
12114
  const found = [];
11651
12115
  try {
11652
12116
  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);
12117
+ const nodeModulesPath = path30.join(root, "node_modules", "@kuma-templates");
12118
+ if (!fs28.existsSync(nodeModulesPath)) return found;
12119
+ const packages = fs28.readdirSync(nodeModulesPath);
11656
12120
  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");
12121
+ const pkgPath = path30.join(nodeModulesPath, pkg, "package.json");
12122
+ if (!fs28.existsSync(pkgPath)) continue;
12123
+ const pkgJson = JSON.parse(fs28.readFileSync(pkgPath, "utf-8"));
12124
+ const templatePath = path30.join(nodeModulesPath, pkg, "template.json");
11661
12125
  let nodeCount = 0, edgeCount = 0;
11662
- if (fs27.existsSync(templatePath)) {
11663
- const tmpl = JSON.parse(fs27.readFileSync(templatePath, "utf-8"));
12126
+ if (fs28.existsSync(templatePath)) {
12127
+ const tmpl = JSON.parse(fs28.readFileSync(templatePath, "utf-8"));
11664
12128
  nodeCount = tmpl.nodes?.length || 0;
11665
12129
  edgeCount = tmpl.edges?.length || 0;
11666
12130
  }
@@ -11745,8 +12209,8 @@ async function tryInstallFromNpm(templateId) {
11745
12209
  const name = templateId.replace(/^graph:/, "");
11746
12210
  try {
11747
12211
  const root = getProjectRoot();
11748
- const pkgPath = path28.join(root, "node_modules", "@kuma-templates", name, "template.json");
11749
- if (fs27.existsSync(pkgPath)) {
12212
+ const pkgPath = path30.join(root, "node_modules", "@kuma-templates", name, "template.json");
12213
+ if (fs28.existsSync(pkgPath)) {
11750
12214
  return await loadNpmTemplate(pkgPath, name);
11751
12215
  }
11752
12216
  } catch {
@@ -11754,14 +12218,14 @@ async function tryInstallFromNpm(templateId) {
11754
12218
  try {
11755
12219
  const npmPackage = `@kuma-templates/${name}`;
11756
12220
  const root = getProjectRoot();
11757
- execSync10(`npm install --no-save ${npmPackage}`, {
12221
+ execSync12(`npm install --no-save ${npmPackage}`, {
11758
12222
  cwd: root,
11759
12223
  encoding: "utf-8",
11760
12224
  timeout: 3e4,
11761
12225
  stdio: "pipe"
11762
12226
  });
11763
- const templatePath = path28.join(root, "node_modules", "@kuma-templates", name, "template.json");
11764
- if (fs27.existsSync(templatePath)) {
12227
+ const templatePath = path30.join(root, "node_modules", "@kuma-templates", name, "template.json");
12228
+ if (fs28.existsSync(templatePath)) {
11765
12229
  return await loadNpmTemplate(templatePath, name);
11766
12230
  }
11767
12231
  return [
@@ -11779,7 +12243,7 @@ async function tryInstallFromNpm(templateId) {
11779
12243
  }
11780
12244
  }
11781
12245
  async function loadNpmTemplate(templatePath, name) {
11782
- const raw = fs27.readFileSync(templatePath, "utf-8");
12246
+ const raw = fs28.readFileSync(templatePath, "utf-8");
11783
12247
  const template = JSON.parse(raw);
11784
12248
  const db = await getDb();
11785
12249
  let nodeCount = 0;
@@ -12166,8 +12630,12 @@ async function handleCore(action, params) {
12166
12630
  return await handleBatchFileWriter(params);
12167
12631
  case "lsp":
12168
12632
  return await handleLspQuery({ ...params, action: params.lspAction });
12633
+ case "find":
12634
+ return await handleKumaFind(params);
12635
+ case "stats":
12636
+ return await handleKumaStats(params);
12169
12637
  default:
12170
- return `\u26A0\uFE0F Unknown action "${action}" for kuma_core. Use: grep, read, edit, batch, lsp`;
12638
+ return `\u26A0\uFE0F Unknown action "${action}" for kuma_core. Use: grep, read, edit, batch, lsp, find, stats`;
12171
12639
  }
12172
12640
  }
12173
12641
  async function handleVerify(action, params) {
@@ -12551,9 +13019,9 @@ function registerAllTools(server) {
12551
13019
  });
12552
13020
  server.tool(
12553
13021
  "kuma_core",
12554
- "Core coding tools: grep (search), read (file), edit (search-and-replace), batch (create files), lsp (rename/reference).",
13022
+ "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
13023
  {
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"),
13024
+ 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
13025
  // grep params
12558
13026
  query: z.string().optional().describe("Regex pattern for grep action"),
12559
13027
  queries: z.array(z.string()).optional().describe("Multiple regex patterns (OR'd together, single pass)"),
@@ -12571,13 +13039,14 @@ function registerAllTools(server) {
12571
13039
  endLine: z.number().min(1).optional().describe("End line (1-indexed) for read"),
12572
13040
  chunkStrategy: z.enum(["full", "smart", "outline"]).optional().default("smart").describe("Read strategy"),
12573
13041
  readOutputMode: z.enum(["rich", "raw"]).optional().default("rich").describe("Output format for read: rich=line numbers, raw=content only"),
12574
- // edit params
13042
+ // edit params — BATCH SUPPORTED: pass multiple edits[] in 1 call (faster than calling edit 5x)
12575
13043
  edits: z.array(z.object({
12576
13044
  searchBlock: z.string().min(1).describe("Code to replace"),
12577
13045
  replaceBlock: z.string().describe("Replacement code"),
12578
13046
  allowMultiple: z.boolean().optional().default(false).describe("Allow multiple replacements"),
12579
13047
  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)"),
13048
+ })).min(1).max(10).optional().describe("Array of edits (for edit action) \u2014 batch multiple edits in 1 call for speed"),
13049
+ 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
13050
  dryRun: z.boolean().optional().default(false).describe("Preview without writing"),
12582
13051
  version: z.union([z.number().min(1), z.literal("list")]).optional().describe("Backup version for rollback"),
12583
13052
  scope: z.enum(["file", "dir", "edit-id", "commit"]).optional().describe("Rollback scope"),
@@ -12588,6 +13057,12 @@ function registerAllTools(server) {
12588
13057
  content: z.string().describe("File content"),
12589
13058
  instructions: z.string().min(1).describe("Reason for creating")
12590
13059
  })).min(1).max(15).optional().describe("Array of files (for batch action)"),
13060
+ // find params
13061
+ name: z.string().optional().describe("File name glob pattern for find action (e.g. '*seed*', '*.tsx')"),
13062
+ path: z.string().optional().describe("Path glob pattern for find action (e.g. '**/utils/**', '*/seed*')"),
13063
+ type: z.enum(["file", "dir", "both"]).optional().default("file").describe("Type filter for find: file=files only, dir=dirs only"),
13064
+ // stats params
13065
+ target: z.enum(["file", "dir", "project"]).optional().default("project").describe("Stats target: file=single file, dir=directory, project=entire project"),
12591
13066
  // lsp params
12592
13067
  line: z.number().min(0).optional().describe("Line number (0-indexed) for LSP"),
12593
13068
  character: z.number().min(0).optional().describe("Character position (0-indexed) for LSP"),
@@ -12923,23 +13398,23 @@ async function main() {
12923
13398
  });
12924
13399
  const output = formatInitResults(results);
12925
13400
  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(
13401
+ const fs29 = await import("fs");
13402
+ const path31 = await import("path");
13403
+ const matchaSkills = path31.resolve(process.cwd(), "skills/matcha/SKILL.md");
13404
+ const matchaAgents = path31.resolve(
12930
13405
  process.cwd(),
12931
13406
  ".agents/skills/matcha/SKILL.md"
12932
13407
  );
12933
- const matchaRootSkills = path29.resolve(
13408
+ const matchaRootSkills = path31.resolve(
12934
13409
  process.cwd(),
12935
13410
  "skills/matcha/SKILL.md"
12936
13411
  );
12937
- const matchaAgentsMd = path29.resolve(process.cwd(), "AGENTS.md");
12938
- const matchaWindsurfRules = path29.resolve(
13412
+ const matchaAgentsMd = path31.resolve(process.cwd(), "AGENTS.md");
13413
+ const matchaWindsurfRules = path31.resolve(
12939
13414
  process.cwd(),
12940
13415
  ".windsurfrules"
12941
13416
  );
12942
- if (fs28.existsSync(matchaSkills) || fs28.existsSync(matchaAgents) || fs28.existsSync(matchaRootSkills) || fs28.existsSync(matchaAgentsMd) || fs28.existsSync(matchaWindsurfRules)) {
13417
+ if (fs29.existsSync(matchaSkills) || fs29.existsSync(matchaAgents) || fs29.existsSync(matchaRootSkills) || fs29.existsSync(matchaAgentsMd) || fs29.existsSync(matchaWindsurfRules)) {
12943
13418
  console.error(
12944
13419
  "\n\u{1F375} Hey, I see matcha is installed \u2014 they pair well together!"
12945
13420
  );
@@ -12953,13 +13428,13 @@ async function main() {
12953
13428
  (async () => {
12954
13429
  try {
12955
13430
  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");
13431
+ const fs29 = await import("fs");
13432
+ const path31 = await import("path");
13433
+ const initMdPath = path31.resolve(process.cwd(), ".kuma/init.md");
13434
+ if (!fs29.existsSync(initMdPath)) {
13435
+ const kumaDir = path31.dirname(initMdPath);
13436
+ if (!fs29.existsSync(kumaDir)) fs29.mkdirSync(kumaDir, { recursive: true });
13437
+ fs29.writeFileSync(initMdPath, generateInitMdContent(), "utf-8");
12963
13438
  console.error(`[${SERVER_NAME}] Auto-generated .kuma/init.md`);
12964
13439
  }
12965
13440
  } catch (err) {
@@ -12970,8 +13445,8 @@ async function main() {
12970
13445
  try {
12971
13446
  const { detectAgent, getSkillPath, getAgentLabel } = await import("./agentDetector-HROEABDO.js");
12972
13447
  const { generateSkill, getSecondaryFiles } = await import("./skillGenerator-F6YO4H5D.js");
12973
- const fs28 = await import("fs");
12974
- const path29 = await import("path");
13448
+ const fs29 = await import("fs");
13449
+ const path31 = await import("path");
12975
13450
  const detection = detectAgent();
12976
13451
  if (!detection.primary) {
12977
13452
  console.error(`[${SERVER_NAME}] No AI agent detected \u2014 skipping auto-skill creation`);
@@ -12979,22 +13454,22 @@ async function main() {
12979
13454
  }
12980
13455
  const agentType = detection.primary;
12981
13456
  const skillPath = getSkillPath(agentType);
12982
- const fullPath = path29.resolve(process.cwd(), skillPath);
12983
- if (fs28.existsSync(fullPath)) {
13457
+ const fullPath = path31.resolve(process.cwd(), skillPath);
13458
+ if (fs29.existsSync(fullPath)) {
12984
13459
  console.error(`[${SERVER_NAME}] Skill exists for ${getAgentLabel(agentType)} \u2014 skipping`);
12985
13460
  return;
12986
13461
  }
12987
- const dir = path29.dirname(fullPath);
12988
- if (!fs28.existsSync(dir)) fs28.mkdirSync(dir, { recursive: true });
12989
- fs28.writeFileSync(fullPath, generateSkill(agentType), "utf-8");
13462
+ const dir = path31.dirname(fullPath);
13463
+ if (!fs29.existsSync(dir)) fs29.mkdirSync(dir, { recursive: true });
13464
+ fs29.writeFileSync(fullPath, generateSkill(agentType), "utf-8");
12990
13465
  console.error(`[${SERVER_NAME}] Auto-created ${skillPath} for ${getAgentLabel(agentType)}`);
12991
13466
  const secondaryFiles = getSecondaryFiles(agentType);
12992
13467
  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");
13468
+ const sfPath = path31.resolve(process.cwd(), sf.path);
13469
+ const sfDir = path31.dirname(sfPath);
13470
+ if (!fs29.existsSync(sfPath)) {
13471
+ if (!fs29.existsSync(sfDir)) fs29.mkdirSync(sfDir, { recursive: true });
13472
+ fs29.writeFileSync(sfPath, sf.content, "utf-8");
12998
13473
  console.error(`[${SERVER_NAME}] Auto-created ${sf.path}`);
12999
13474
  }
13000
13475
  }