make-laten 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +499 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +499 -26
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.js +143 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +143 -3
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/server.js +148 -8
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.mjs +150 -10
- package/dist/mcp/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1695,36 +1695,424 @@ var GitStatusCompressor = class {
|
|
|
1695
1695
|
}
|
|
1696
1696
|
};
|
|
1697
1697
|
|
|
1698
|
-
// src/
|
|
1698
|
+
// src/route/tool-router.ts
|
|
1699
|
+
var ToolRouter = class {
|
|
1700
|
+
rules = [
|
|
1701
|
+
{
|
|
1702
|
+
matcher: (input) => input.type === "file",
|
|
1703
|
+
compressor: "file-read",
|
|
1704
|
+
confidence: 0.95,
|
|
1705
|
+
reason: "File read detected"
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
matcher: (input) => input.type === "grep",
|
|
1709
|
+
compressor: "grep",
|
|
1710
|
+
confidence: 0.95,
|
|
1711
|
+
reason: "Grep results detected"
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
matcher: (input) => input.type === "git-diff",
|
|
1715
|
+
compressor: "git-diff",
|
|
1716
|
+
confidence: 0.95,
|
|
1717
|
+
reason: "Git diff detected"
|
|
1718
|
+
}
|
|
1719
|
+
];
|
|
1720
|
+
route(input) {
|
|
1721
|
+
for (const rule of this.rules) {
|
|
1722
|
+
if (rule.matcher(input)) {
|
|
1723
|
+
return {
|
|
1724
|
+
tool: "compress",
|
|
1725
|
+
compressor: rule.compressor,
|
|
1726
|
+
confidence: rule.confidence,
|
|
1727
|
+
reason: rule.reason
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
return {
|
|
1732
|
+
tool: "compress",
|
|
1733
|
+
confidence: 0.1,
|
|
1734
|
+
reason: "No matching route found"
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
addRule(rule) {
|
|
1738
|
+
this.rules.push(rule);
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
// src/route/strategy-router.ts
|
|
1743
|
+
var StrategyRouter = class {
|
|
1744
|
+
thresholds = {
|
|
1745
|
+
small: 500,
|
|
1746
|
+
large: 5e3
|
|
1747
|
+
};
|
|
1748
|
+
select(context) {
|
|
1749
|
+
if (context.userPreference) {
|
|
1750
|
+
return {
|
|
1751
|
+
strategy: context.userPreference,
|
|
1752
|
+
reason: `User preference: ${context.userPreference}`,
|
|
1753
|
+
savings: this.estimateSavings(context.userPreference)
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
const fileSize = context.fileSize || 0;
|
|
1757
|
+
if (fileSize >= this.thresholds.large) {
|
|
1758
|
+
return {
|
|
1759
|
+
strategy: "aggressive",
|
|
1760
|
+
reason: `large file (${fileSize} bytes)`,
|
|
1761
|
+
savings: 0.6
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
if (fileSize <= this.thresholds.small) {
|
|
1765
|
+
return {
|
|
1766
|
+
strategy: "conservative",
|
|
1767
|
+
reason: `small file (${fileSize} bytes)`,
|
|
1768
|
+
savings: 0.2
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
return {
|
|
1772
|
+
strategy: "balanced",
|
|
1773
|
+
reason: `Medium file (${fileSize} bytes)`,
|
|
1774
|
+
savings: 0.4
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
estimateSavings(strategy) {
|
|
1778
|
+
switch (strategy) {
|
|
1779
|
+
case "aggressive":
|
|
1780
|
+
return 0.6;
|
|
1781
|
+
case "balanced":
|
|
1782
|
+
return 0.4;
|
|
1783
|
+
case "conservative":
|
|
1784
|
+
return 0.2;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
// src/learn/pattern-miner.ts
|
|
1699
1790
|
var import_fs = __toESM(require("fs"));
|
|
1700
1791
|
var import_path3 = __toESM(require("path"));
|
|
1792
|
+
var DATA_FILE = import_path3.default.join(process.env.HOME || "~", ".make-laten", "patterns.json");
|
|
1793
|
+
var PatternMiner = class {
|
|
1794
|
+
operations = [];
|
|
1795
|
+
patterns = /* @__PURE__ */ new Map();
|
|
1796
|
+
persistencePath;
|
|
1797
|
+
lastPersist = 0;
|
|
1798
|
+
constructor(options) {
|
|
1799
|
+
this.persistencePath = options?.persistencePath || DATA_FILE;
|
|
1800
|
+
this.loadFromDisk();
|
|
1801
|
+
}
|
|
1802
|
+
loadFromDisk() {
|
|
1803
|
+
try {
|
|
1804
|
+
if (import_fs.default.existsSync(this.persistencePath)) {
|
|
1805
|
+
const data = JSON.parse(import_fs.default.readFileSync(this.persistencePath, "utf-8"));
|
|
1806
|
+
if (Array.isArray(data.patterns)) {
|
|
1807
|
+
for (const p of data.patterns) {
|
|
1808
|
+
this.patterns.set(p.type, p);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (Array.isArray(data.operations)) {
|
|
1812
|
+
this.operations = data.operations.slice(-100);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
} catch {
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
persistToDisk() {
|
|
1819
|
+
const now = Date.now();
|
|
1820
|
+
if (now - this.lastPersist < 5e3) return;
|
|
1821
|
+
this.lastPersist = now;
|
|
1822
|
+
try {
|
|
1823
|
+
const dir = import_path3.default.dirname(this.persistencePath);
|
|
1824
|
+
if (!import_fs.default.existsSync(dir)) import_fs.default.mkdirSync(dir, { recursive: true });
|
|
1825
|
+
import_fs.default.writeFileSync(this.persistencePath, JSON.stringify({
|
|
1826
|
+
patterns: Array.from(this.patterns.values()),
|
|
1827
|
+
operations: this.operations.slice(-100)
|
|
1828
|
+
}, null, 2));
|
|
1829
|
+
} catch {
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
record(operation) {
|
|
1833
|
+
this.operations.push(operation);
|
|
1834
|
+
if (this.operations.length > 100) this.operations.shift();
|
|
1835
|
+
if (operation.success) {
|
|
1836
|
+
this.updatePatterns(operation);
|
|
1837
|
+
}
|
|
1838
|
+
this.persistToDisk();
|
|
1839
|
+
}
|
|
1840
|
+
getPatterns() {
|
|
1841
|
+
return Array.from(this.patterns.values());
|
|
1842
|
+
}
|
|
1843
|
+
getPatternsByType(type) {
|
|
1844
|
+
return this.getPatterns().filter((p) => p.type === type);
|
|
1845
|
+
}
|
|
1846
|
+
updatePatterns(operation) {
|
|
1847
|
+
const key = operation.type;
|
|
1848
|
+
const existing = this.patterns.get(key);
|
|
1849
|
+
if (existing) {
|
|
1850
|
+
existing.count++;
|
|
1851
|
+
existing.confidence = Math.min(0.99, existing.confidence + 0.05);
|
|
1852
|
+
} else {
|
|
1853
|
+
this.patterns.set(key, {
|
|
1854
|
+
id: `p${this.patterns.size + 1}`,
|
|
1855
|
+
type: operation.type,
|
|
1856
|
+
pattern: JSON.stringify(operation.input),
|
|
1857
|
+
confidence: 0.5,
|
|
1858
|
+
count: 1
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
|
|
1864
|
+
// src/learn/failure-learner.ts
|
|
1865
|
+
var import_fs2 = __toESM(require("fs"));
|
|
1866
|
+
var import_path4 = __toESM(require("path"));
|
|
1867
|
+
var DATA_FILE2 = import_path4.default.join(process.env.HOME || "~", ".make-laten", "failures.json");
|
|
1868
|
+
var FailureLearner = class {
|
|
1869
|
+
failures = /* @__PURE__ */ new Map();
|
|
1870
|
+
persistencePath;
|
|
1871
|
+
lastPersist = 0;
|
|
1872
|
+
constructor(options) {
|
|
1873
|
+
this.persistencePath = options?.persistencePath || DATA_FILE2;
|
|
1874
|
+
this.loadFromDisk();
|
|
1875
|
+
}
|
|
1876
|
+
loadFromDisk() {
|
|
1877
|
+
try {
|
|
1878
|
+
if (import_fs2.default.existsSync(this.persistencePath)) {
|
|
1879
|
+
const data = JSON.parse(import_fs2.default.readFileSync(this.persistencePath, "utf-8"));
|
|
1880
|
+
if (Array.isArray(data.failures)) {
|
|
1881
|
+
for (const f of data.failures) {
|
|
1882
|
+
this.failures.set(`${f.type}:${f.error}`, f);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
} catch {
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
persistToDisk() {
|
|
1890
|
+
const now = Date.now();
|
|
1891
|
+
if (now - this.lastPersist < 5e3) return;
|
|
1892
|
+
this.lastPersist = now;
|
|
1893
|
+
try {
|
|
1894
|
+
const dir = import_path4.default.dirname(this.persistencePath);
|
|
1895
|
+
if (!import_fs2.default.existsSync(dir)) import_fs2.default.mkdirSync(dir, { recursive: true });
|
|
1896
|
+
import_fs2.default.writeFileSync(this.persistencePath, JSON.stringify({
|
|
1897
|
+
failures: Array.from(this.failures.values())
|
|
1898
|
+
}, null, 2));
|
|
1899
|
+
} catch {
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
record(failure) {
|
|
1903
|
+
const key = `${failure.type}:${failure.error}`;
|
|
1904
|
+
const existing = this.failures.get(key);
|
|
1905
|
+
if (existing) {
|
|
1906
|
+
existing.count++;
|
|
1907
|
+
} else {
|
|
1908
|
+
this.failures.set(key, {
|
|
1909
|
+
id: `f${this.failures.size + 1}`,
|
|
1910
|
+
...failure,
|
|
1911
|
+
timestamp: Date.now(),
|
|
1912
|
+
count: 1
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
this.persistToDisk();
|
|
1916
|
+
}
|
|
1917
|
+
getFailures() {
|
|
1918
|
+
return Array.from(this.failures.values());
|
|
1919
|
+
}
|
|
1920
|
+
getFailuresByType(type) {
|
|
1921
|
+
return this.getFailures().filter((f) => f.type === type);
|
|
1922
|
+
}
|
|
1923
|
+
getSuggestions(type) {
|
|
1924
|
+
const failures = Array.from(this.failures.values()).filter((f) => f.type === type);
|
|
1925
|
+
const suggestions = [];
|
|
1926
|
+
for (const failure of failures) {
|
|
1927
|
+
if (failure.error.includes("not found")) {
|
|
1928
|
+
suggestions.push("check if file exists before processing");
|
|
1929
|
+
}
|
|
1930
|
+
if (failure.error.includes("permission")) {
|
|
1931
|
+
suggestions.push("verify file permissions");
|
|
1932
|
+
}
|
|
1933
|
+
if (failure.count > 2) {
|
|
1934
|
+
suggestions.push(`recurring issue: ${failure.error}`);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return [...new Set(suggestions)];
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1940
|
+
|
|
1941
|
+
// src/correct/auto-correct.ts
|
|
1942
|
+
var import_fs3 = __toESM(require("fs"));
|
|
1943
|
+
var import_path5 = __toESM(require("path"));
|
|
1944
|
+
var DEFAULT_RULES = [
|
|
1945
|
+
// Common typos
|
|
1946
|
+
{ name: "typo-teh", description: "Common typo", pattern: "teh", replacement: "the" },
|
|
1947
|
+
{ name: "typo-adn", description: "Common typo", pattern: "adn", replacement: "and" },
|
|
1948
|
+
{ name: "typo-fo", description: "Common typo", pattern: " fo ", replacement: " of " },
|
|
1949
|
+
{ name: "typo-ot", description: "Common typo", pattern: " ot ", replacement: " to " },
|
|
1950
|
+
{ name: "typo-isnt", description: "Common typo", pattern: "isnt", replacement: "isn't" },
|
|
1951
|
+
{ name: "typo-dont", description: "Common typo", pattern: "dont", replacement: "don't" },
|
|
1952
|
+
{ name: "typo-cant", description: "Common typo", pattern: "cant", replacement: "can't" },
|
|
1953
|
+
{ name: "typo-wont", description: "Common typo", pattern: "wont", replacement: "won't" },
|
|
1954
|
+
{ name: "typo-youre", description: "Common typo", pattern: "youre", replacement: "you're" },
|
|
1955
|
+
{ name: "typo-its", description: "Common typo", pattern: "its a", replacement: "it's a" },
|
|
1956
|
+
// Code patterns
|
|
1957
|
+
{ name: "code-console-log", description: "Remove console.log", pattern: /console\.log\([^)]*\);?\n?/g, replacement: "" },
|
|
1958
|
+
{ name: "code-debugger", description: "Remove debugger", pattern: /debugger;?\n?/g, replacement: "" },
|
|
1959
|
+
{ name: "code-var-let", description: "var to let", pattern: /\bvar\b/g, replacement: "let" },
|
|
1960
|
+
// Markdown
|
|
1961
|
+
{ name: "md-double-space", description: "Double space to single", pattern: " ", replacement: " " }
|
|
1962
|
+
];
|
|
1963
|
+
var DATA_FILE3 = import_path5.default.join(process.env.HOME || "~", ".make-laten", "corrections.json");
|
|
1964
|
+
var AutoCorrect = class {
|
|
1965
|
+
rules;
|
|
1966
|
+
corrections = [];
|
|
1967
|
+
appliedCount = 0;
|
|
1968
|
+
persistencePath;
|
|
1969
|
+
constructor(options) {
|
|
1970
|
+
this.persistencePath = options?.persistencePath || DATA_FILE3;
|
|
1971
|
+
this.rules = [...DEFAULT_RULES];
|
|
1972
|
+
this.loadPersistedRules();
|
|
1973
|
+
}
|
|
1974
|
+
loadPersistedRules() {
|
|
1975
|
+
try {
|
|
1976
|
+
if (import_fs3.default.existsSync(this.persistencePath)) {
|
|
1977
|
+
const data = JSON.parse(import_fs3.default.readFileSync(this.persistencePath, "utf-8"));
|
|
1978
|
+
if (Array.isArray(data.customRules)) {
|
|
1979
|
+
this.rules.push(...data.customRules);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
} catch {
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
persistRules() {
|
|
1986
|
+
try {
|
|
1987
|
+
const dir = import_path5.default.dirname(this.persistencePath);
|
|
1988
|
+
if (!import_fs3.default.existsSync(dir)) import_fs3.default.mkdirSync(dir, { recursive: true });
|
|
1989
|
+
const existing = this.loadExistingData();
|
|
1990
|
+
existing.customRules = this.rules.filter((r) => !DEFAULT_RULES.includes(r));
|
|
1991
|
+
import_fs3.default.writeFileSync(this.persistencePath, JSON.stringify(existing, null, 2));
|
|
1992
|
+
} catch {
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
loadExistingData() {
|
|
1996
|
+
try {
|
|
1997
|
+
if (import_fs3.default.existsSync(this.persistencePath)) {
|
|
1998
|
+
return JSON.parse(import_fs3.default.readFileSync(this.persistencePath, "utf-8"));
|
|
1999
|
+
}
|
|
2000
|
+
} catch {
|
|
2001
|
+
}
|
|
2002
|
+
return { customRules: [] };
|
|
2003
|
+
}
|
|
2004
|
+
addRule(rule) {
|
|
2005
|
+
this.rules.push(rule);
|
|
2006
|
+
this.persistRules();
|
|
2007
|
+
}
|
|
2008
|
+
getRules() {
|
|
2009
|
+
return this.rules;
|
|
2010
|
+
}
|
|
2011
|
+
correct(input) {
|
|
2012
|
+
let output = input;
|
|
2013
|
+
let changed = false;
|
|
2014
|
+
for (const rule of this.rules) {
|
|
2015
|
+
if (typeof rule.pattern === "string") {
|
|
2016
|
+
if (output.includes(rule.pattern)) {
|
|
2017
|
+
output = output.split(rule.pattern).join(rule.replacement);
|
|
2018
|
+
changed = true;
|
|
2019
|
+
}
|
|
2020
|
+
} else {
|
|
2021
|
+
const matches = output.match(rule.pattern);
|
|
2022
|
+
if (matches) {
|
|
2023
|
+
output = output.replace(rule.pattern, rule.replacement);
|
|
2024
|
+
changed = true;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
if (changed) {
|
|
2029
|
+
this.recordCorrection(input, output, "auto");
|
|
2030
|
+
}
|
|
2031
|
+
return output;
|
|
2032
|
+
}
|
|
2033
|
+
getStats() {
|
|
2034
|
+
return {
|
|
2035
|
+
applied: this.appliedCount,
|
|
2036
|
+
corrections: this.corrections
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
recordCorrection(original, corrected, ruleName) {
|
|
2040
|
+
this.appliedCount++;
|
|
2041
|
+
this.corrections.push({
|
|
2042
|
+
id: `c${this.corrections.length + 1}`,
|
|
2043
|
+
original,
|
|
2044
|
+
corrected,
|
|
2045
|
+
rule: ruleName,
|
|
2046
|
+
confidence: 0.9
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// src/cli/commands/benchmark.ts
|
|
2052
|
+
var import_fs4 = __toESM(require("fs"));
|
|
2053
|
+
var import_path6 = __toESM(require("path"));
|
|
1701
2054
|
var import_child_process5 = require("child_process");
|
|
2055
|
+
function countTokens(text) {
|
|
2056
|
+
return Math.ceil(text.length / 4);
|
|
2057
|
+
}
|
|
1702
2058
|
async function benchmark() {
|
|
1703
|
-
const fileCompressor = new FileReadCompressor();
|
|
1704
|
-
const diffCompressor = new GitDiffCompressor();
|
|
1705
|
-
const statusCompressor = new GitStatusCompressor();
|
|
1706
2059
|
const projectPath = process.cwd();
|
|
1707
2060
|
console.log("");
|
|
1708
|
-
console.log(" make-laten benchmark");
|
|
1709
|
-
console.log("
|
|
2061
|
+
console.log(" make-laten benchmark (17 tools)");
|
|
2062
|
+
console.log(" ================================");
|
|
1710
2063
|
console.log("");
|
|
1711
2064
|
const results = [];
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
const content =
|
|
1717
|
-
const raw =
|
|
1718
|
-
const
|
|
1719
|
-
const
|
|
1720
|
-
|
|
2065
|
+
console.log(" Compress Layer");
|
|
2066
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2067
|
+
const smallFile = import_path6.default.join(projectPath, "src/index.ts");
|
|
2068
|
+
if (import_fs4.default.existsSync(smallFile)) {
|
|
2069
|
+
const content = import_fs4.default.readFileSync(smallFile, "utf-8");
|
|
2070
|
+
const raw = countTokens(content);
|
|
2071
|
+
const compressor = new FileReadCompressor();
|
|
2072
|
+
const result = await compressor.compress({ content, filePath: "src/index.ts" });
|
|
2073
|
+
const compressed = countTokens(result.content);
|
|
2074
|
+
results.push({ name: "read (small)", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
2075
|
+
}
|
|
2076
|
+
const mediumFile = import_path6.default.join(projectPath, "src/mcp/server.ts");
|
|
2077
|
+
if (import_fs4.default.existsSync(mediumFile)) {
|
|
2078
|
+
const content = import_fs4.default.readFileSync(mediumFile, "utf-8");
|
|
2079
|
+
const raw = countTokens(content);
|
|
2080
|
+
const compressor = new FileReadCompressor();
|
|
2081
|
+
const result = await compressor.compress({ content, filePath: "src/mcp/server.ts" });
|
|
2082
|
+
const compressed = countTokens(result.content);
|
|
2083
|
+
results.push({ name: "read (medium)", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
2084
|
+
}
|
|
2085
|
+
const largeFile = import_path6.default.join(projectPath, "README.md");
|
|
2086
|
+
if (import_fs4.default.existsSync(largeFile)) {
|
|
2087
|
+
const content = import_fs4.default.readFileSync(largeFile, "utf-8");
|
|
2088
|
+
const raw = countTokens(content);
|
|
2089
|
+
const compressor = new FileReadCompressor();
|
|
2090
|
+
const result = await compressor.compress({ content, filePath: "README.md" });
|
|
2091
|
+
const compressed = countTokens(result.content);
|
|
2092
|
+
results.push({ name: "read (large)", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
2093
|
+
}
|
|
2094
|
+
try {
|
|
2095
|
+
const grepResult = (0, import_child_process5.execSync)('grep -rn "export" src/ 2>/dev/null | head -30', { cwd: projectPath, encoding: "utf-8" }).trim();
|
|
2096
|
+
if (grepResult) {
|
|
2097
|
+
const raw = countTokens(grepResult);
|
|
2098
|
+
const matches = grepResult.split("\n").filter(Boolean).map((line) => {
|
|
2099
|
+
const parts = line.split(":");
|
|
2100
|
+
return { file: parts[0], line: parseInt(parts[1], 10) || 0, content: parts.slice(2).join(":").trim() };
|
|
2101
|
+
});
|
|
2102
|
+
const compressor = new GrepCompressor();
|
|
2103
|
+
const result = await compressor.compress({ results: matches, pattern: "export", directory: "src/" });
|
|
2104
|
+
const compressed = countTokens(result.content);
|
|
2105
|
+
results.push({ name: "grep", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
2106
|
+
}
|
|
2107
|
+
} catch {
|
|
1721
2108
|
}
|
|
1722
2109
|
try {
|
|
1723
2110
|
const diff = (0, import_child_process5.execSync)('git diff HEAD~1 2>/dev/null || echo ""', { cwd: projectPath, encoding: "utf-8" }).trim();
|
|
1724
2111
|
if (diff) {
|
|
1725
|
-
const raw =
|
|
1726
|
-
const
|
|
1727
|
-
const
|
|
2112
|
+
const raw = countTokens(diff);
|
|
2113
|
+
const compressor = new GitDiffCompressor();
|
|
2114
|
+
const result = await compressor.compress({ diff });
|
|
2115
|
+
const compressed = countTokens(result.content);
|
|
1728
2116
|
results.push({ name: "git-diff", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
1729
2117
|
}
|
|
1730
2118
|
} catch {
|
|
@@ -1732,23 +2120,108 @@ async function benchmark() {
|
|
|
1732
2120
|
try {
|
|
1733
2121
|
const status = (0, import_child_process5.execSync)('git status --porcelain 2>/dev/null || echo ""', { cwd: projectPath, encoding: "utf-8" }).trim();
|
|
1734
2122
|
if (status) {
|
|
1735
|
-
const raw =
|
|
1736
|
-
const
|
|
1737
|
-
const
|
|
2123
|
+
const raw = countTokens(status);
|
|
2124
|
+
const compressor = new GitStatusCompressor();
|
|
2125
|
+
const result = await compressor.compress({ status });
|
|
2126
|
+
const compressed = countTokens(result.content);
|
|
1738
2127
|
results.push({ name: "git-status", raw, compressed, savings: Math.round((1 - compressed / raw) * 100) });
|
|
1739
2128
|
}
|
|
1740
2129
|
} catch {
|
|
1741
2130
|
}
|
|
1742
|
-
console.log("
|
|
1743
|
-
console.log("
|
|
1744
|
-
|
|
1745
|
-
|
|
2131
|
+
console.log("");
|
|
2132
|
+
console.log(" Route Layer");
|
|
2133
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2134
|
+
const router = new ToolRouter();
|
|
2135
|
+
const strategyRouter = new StrategyRouter();
|
|
2136
|
+
const routeTests = [
|
|
2137
|
+
{ type: "file", content: "test" },
|
|
2138
|
+
{ type: "grep", content: "test" },
|
|
2139
|
+
{ type: "git-diff", content: "test" }
|
|
2140
|
+
];
|
|
2141
|
+
for (const test of routeTests) {
|
|
2142
|
+
const route = router.route(test);
|
|
2143
|
+
console.log(` route(${test.type}) \u2192 ${route.compressor || "none"} (${Math.round(route.confidence * 100)}%)`);
|
|
2144
|
+
}
|
|
2145
|
+
const strategies = [
|
|
2146
|
+
{ fileSize: 100, pref: void 0 },
|
|
2147
|
+
{ fileSize: 1e3, pref: void 0 },
|
|
2148
|
+
{ fileSize: 1e4, pref: void 0 },
|
|
2149
|
+
{ fileSize: 0, pref: "aggressive" }
|
|
2150
|
+
];
|
|
2151
|
+
for (const s of strategies) {
|
|
2152
|
+
const strategy = strategyRouter.select({ fileSize: s.fileSize, userPreference: s.pref });
|
|
2153
|
+
console.log(` strategy(${s.fileSize || s.pref}) \u2192 ${strategy.strategy} (savings: ${Math.round(strategy.savings * 100)}%)`);
|
|
2154
|
+
}
|
|
2155
|
+
console.log("");
|
|
2156
|
+
console.log(" Cache Layer");
|
|
2157
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2158
|
+
const cache = new SessionCache();
|
|
2159
|
+
cache.set("test:key1", { content: "value1", metadata: {} });
|
|
2160
|
+
cache.set("test:key2", { content: "value2", metadata: {} });
|
|
2161
|
+
cache.get("test:key1");
|
|
2162
|
+
cache.get("test:key1");
|
|
2163
|
+
cache.get("test:key2");
|
|
2164
|
+
cache.get("test:miss");
|
|
2165
|
+
const cacheStats = cache.stats();
|
|
2166
|
+
console.log(` L1 Session: ${cacheStats.size} entries, ${cacheStats.hits} hits, ${cacheStats.misses} misses, ${Math.round(cacheStats.hitRate * 100)}% hit rate`);
|
|
2167
|
+
console.log("");
|
|
2168
|
+
console.log(" Learn Layer");
|
|
2169
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2170
|
+
const miner = new PatternMiner();
|
|
2171
|
+
miner.record({ type: "file-read", input: { file: "a.ts" }, success: true });
|
|
2172
|
+
miner.record({ type: "file-read", input: { file: "b.ts" }, success: true });
|
|
2173
|
+
miner.record({ type: "grep", input: { pattern: "test" }, success: true });
|
|
2174
|
+
miner.record({ type: "git-diff", input: {}, success: true });
|
|
2175
|
+
const learner = new FailureLearner();
|
|
2176
|
+
learner.record({ type: "file-read", error: "File not found", success: false });
|
|
2177
|
+
learner.record({ type: "grep", error: "Permission denied", success: false });
|
|
2178
|
+
const patterns = miner.getPatterns();
|
|
2179
|
+
const failures = learner.getFailures();
|
|
2180
|
+
console.log(` Patterns: ${patterns.length} learned`);
|
|
2181
|
+
for (const p of patterns.slice(0, 3)) {
|
|
2182
|
+
console.log(` - ${p.type}: confidence ${Math.round(p.confidence * 100)}%, count ${p.count}`);
|
|
2183
|
+
}
|
|
2184
|
+
console.log(` Failures: ${failures.length} recorded`);
|
|
2185
|
+
for (const f of failures.slice(0, 3)) {
|
|
2186
|
+
const suggestions = learner.getSuggestions(f.type);
|
|
2187
|
+
console.log(` - ${f.type}: ${f.error} \u2192 ${suggestions[0] || "none"}`);
|
|
1746
2188
|
}
|
|
2189
|
+
console.log("");
|
|
2190
|
+
console.log(" Correct Layer");
|
|
2191
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2192
|
+
const correct = new AutoCorrect();
|
|
2193
|
+
correct.addRule({ name: "typo", description: "Fix common typo", pattern: "teh", replacement: "the" });
|
|
2194
|
+
correct.addRule({ name: "typo2", description: "Fix common typo", pattern: "adn", replacement: "and" });
|
|
2195
|
+
const testText = "teh quick brown fox adn teh cat";
|
|
2196
|
+
const corrected = correct.correct(testText);
|
|
2197
|
+
const correctStats = correct.getStats();
|
|
2198
|
+
console.log(` Input: "${testText}"`);
|
|
2199
|
+
console.log(` Output: "${corrected}"`);
|
|
2200
|
+
console.log(` Corrections applied: ${correctStats.applied}`);
|
|
2201
|
+
console.log("");
|
|
2202
|
+
console.log(" Web Layer");
|
|
2203
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2204
|
+
const webRouter = new WebRouter();
|
|
2205
|
+
const webStats = webRouter.getCacheStats();
|
|
2206
|
+
console.log(` Cache: ${webStats.fetchSize} fetch, ${webStats.searchSize} search`);
|
|
2207
|
+
console.log(` Backends: duckduckgo (default)`);
|
|
2208
|
+
console.log(` Features: semantic extraction, compression, caching`);
|
|
2209
|
+
console.log("");
|
|
2210
|
+
console.log(" Summary");
|
|
2211
|
+
console.log(" " + "\u2500".repeat(50));
|
|
1747
2212
|
const totalRaw = results.reduce((s, r) => s + r.raw, 0);
|
|
1748
2213
|
const totalCompressed = results.reduce((s, r) => s + r.compressed, 0);
|
|
1749
2214
|
const savings = Math.round((1 - totalCompressed / totalRaw) * 100);
|
|
2215
|
+
console.log(" Command".padEnd(20) + "Raw".padEnd(10) + "Compressed".padEnd(12) + "Savings");
|
|
2216
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2217
|
+
for (const r of results) {
|
|
2218
|
+
console.log(` ${r.name}`.padEnd(20) + `${r.raw}`.padEnd(10) + `${r.compressed}`.padEnd(12) + `${r.savings}%`);
|
|
2219
|
+
}
|
|
2220
|
+
console.log(" " + "\u2500".repeat(50));
|
|
2221
|
+
console.log(` ${"Total".padEnd(20)}${totalRaw}`.padEnd(10) + `${totalCompressed}`.padEnd(12) + `${savings}%`);
|
|
1750
2222
|
console.log("");
|
|
1751
|
-
console.log(`
|
|
2223
|
+
console.log(` 17 tools exposed via MCP`);
|
|
2224
|
+
console.log(` 4 layers: compress, route, cache, learn`);
|
|
1752
2225
|
console.log("");
|
|
1753
2226
|
}
|
|
1754
2227
|
benchmark();
|