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