make-laten 1.0.0
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/README.md +132 -0
- package/dist/cli/index.js +1302 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +1279 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/index.js +2036 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1946 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +64 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2036 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AGENT_CONFIGS: () => AGENT_CONFIGS,
|
|
34
|
+
AgentDetector: () => AgentDetector,
|
|
35
|
+
AutoCorrect: () => AutoCorrect,
|
|
36
|
+
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
37
|
+
CodexAdapter: () => CodexAdapter,
|
|
38
|
+
CrossSessionCache: () => CrossSessionCache,
|
|
39
|
+
DuckDuckGoBackend: () => DuckDuckGoBackend,
|
|
40
|
+
EdgeStore: () => EdgeStore,
|
|
41
|
+
ErrorHandler: () => ErrorHandler,
|
|
42
|
+
FailureLearner: () => FailureLearner,
|
|
43
|
+
FileReadCompressor: () => FileReadCompressor,
|
|
44
|
+
GeminiCliAdapter: () => GeminiCliAdapter,
|
|
45
|
+
GitDiffCompressor: () => GitDiffCompressor,
|
|
46
|
+
GrepCompressor: () => GrepCompressor,
|
|
47
|
+
HookAdapter: () => HookAdapter,
|
|
48
|
+
Installer: () => Installer,
|
|
49
|
+
Logger: () => Logger,
|
|
50
|
+
MetricsCollector: () => MetricsCollector,
|
|
51
|
+
NodeStore: () => NodeStore,
|
|
52
|
+
OutputCompressor: () => OutputCompressor,
|
|
53
|
+
PatternMiner: () => PatternMiner,
|
|
54
|
+
Pipeline: () => Pipeline,
|
|
55
|
+
PluginLoader: () => PluginLoader,
|
|
56
|
+
RateLimiter: () => RateLimiter,
|
|
57
|
+
RulesAdapter: () => RulesAdapter,
|
|
58
|
+
SemanticCache: () => SemanticCache,
|
|
59
|
+
SemanticTool: () => SemanticTool,
|
|
60
|
+
SessionCache: () => SessionCache,
|
|
61
|
+
SessionManager: () => SessionManager,
|
|
62
|
+
StrategyRouter: () => StrategyRouter,
|
|
63
|
+
ToolRegistry: () => ToolRegistry,
|
|
64
|
+
ToolRouter: () => ToolRouter,
|
|
65
|
+
VERSION: () => VERSION,
|
|
66
|
+
WebRouter: () => WebRouter,
|
|
67
|
+
calculateConfidence: () => calculateConfidence,
|
|
68
|
+
compressWebContent: () => compressWebContent,
|
|
69
|
+
createAdapter: () => createAdapter,
|
|
70
|
+
createCacheManager: () => createCacheManager,
|
|
71
|
+
createCorrector: () => createCorrector,
|
|
72
|
+
createDatabase: () => createDatabase,
|
|
73
|
+
createDetector: () => createDetector,
|
|
74
|
+
createHookAdapter: () => createHookAdapter,
|
|
75
|
+
createInstaller: () => createInstaller,
|
|
76
|
+
createLearner: () => createLearner,
|
|
77
|
+
createRouter: () => createRouter,
|
|
78
|
+
createRulesAdapter: () => createRulesAdapter,
|
|
79
|
+
createToolRegistry: () => createToolRegistry,
|
|
80
|
+
extractSemantic: () => extractSemantic,
|
|
81
|
+
getAdapter: () => getAdapter,
|
|
82
|
+
getAdapters: () => getAdapters,
|
|
83
|
+
getSemanticTools: () => getSemanticTools,
|
|
84
|
+
makeLatenCache: () => makeLatenCache,
|
|
85
|
+
registerAdapter: () => registerAdapter,
|
|
86
|
+
selectBackend: () => selectBackend
|
|
87
|
+
});
|
|
88
|
+
module.exports = __toCommonJS(index_exports);
|
|
89
|
+
|
|
90
|
+
// src/graph/database.ts
|
|
91
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
92
|
+
var SCHEMA = `
|
|
93
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
94
|
+
id TEXT PRIMARY KEY,
|
|
95
|
+
type TEXT NOT NULL,
|
|
96
|
+
content TEXT NOT NULL,
|
|
97
|
+
original TEXT,
|
|
98
|
+
embedding BLOB,
|
|
99
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
100
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
101
|
+
accessed_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
102
|
+
access_count INTEGER DEFAULT 0
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
106
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
107
|
+
source TEXT NOT NULL,
|
|
108
|
+
target TEXT NOT NULL,
|
|
109
|
+
type TEXT NOT NULL,
|
|
110
|
+
weight REAL DEFAULT 1.0,
|
|
111
|
+
metadata TEXT,
|
|
112
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
113
|
+
FOREIGN KEY (source) REFERENCES nodes(id),
|
|
114
|
+
FOREIGN KEY (target) REFERENCES nodes(id)
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
|
|
118
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_created ON nodes(created_at);
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_accessed ON nodes(accessed_at);
|
|
120
|
+
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source);
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_edges_type ON edges(type);
|
|
123
|
+
`;
|
|
124
|
+
async function createDatabase(dbPath) {
|
|
125
|
+
const db = new import_better_sqlite3.default(dbPath);
|
|
126
|
+
db.pragma("journal_mode = WAL");
|
|
127
|
+
db.pragma("foreign_keys = ON");
|
|
128
|
+
db.exec(SCHEMA);
|
|
129
|
+
return db;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/graph/edges.ts
|
|
133
|
+
var EdgeStore = class {
|
|
134
|
+
constructor(db) {
|
|
135
|
+
this.db = db;
|
|
136
|
+
}
|
|
137
|
+
db;
|
|
138
|
+
async add(edge) {
|
|
139
|
+
this.db.prepare(`
|
|
140
|
+
INSERT INTO edges (source, target, type, weight, metadata)
|
|
141
|
+
VALUES (?, ?, ?, ?, ?)
|
|
142
|
+
`).run(
|
|
143
|
+
edge.source,
|
|
144
|
+
edge.target,
|
|
145
|
+
edge.type,
|
|
146
|
+
edge.weight,
|
|
147
|
+
edge.metadata ? JSON.stringify(edge.metadata) : null
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
async getFrom(source, type) {
|
|
151
|
+
let query = "SELECT * FROM edges WHERE source = ?";
|
|
152
|
+
const params = [source];
|
|
153
|
+
if (type) {
|
|
154
|
+
query += " AND type = ?";
|
|
155
|
+
params.push(type);
|
|
156
|
+
}
|
|
157
|
+
const rows = this.db.prepare(query).all(...params);
|
|
158
|
+
return rows.map((row) => this.hydrate(row));
|
|
159
|
+
}
|
|
160
|
+
async getTo(target, type) {
|
|
161
|
+
let query = "SELECT * FROM edges WHERE target = ?";
|
|
162
|
+
const params = [target];
|
|
163
|
+
if (type) {
|
|
164
|
+
query += " AND type = ?";
|
|
165
|
+
params.push(type);
|
|
166
|
+
}
|
|
167
|
+
const rows = this.db.prepare(query).all(...params);
|
|
168
|
+
return rows.map((row) => this.hydrate(row));
|
|
169
|
+
}
|
|
170
|
+
async delete(source, target) {
|
|
171
|
+
this.db.prepare("DELETE FROM edges WHERE source = ? AND target = ?").run(source, target);
|
|
172
|
+
}
|
|
173
|
+
hydrate(row) {
|
|
174
|
+
return {
|
|
175
|
+
id: row.id,
|
|
176
|
+
source: row.source,
|
|
177
|
+
target: row.target,
|
|
178
|
+
type: row.type,
|
|
179
|
+
weight: row.weight,
|
|
180
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
181
|
+
createdAt: row.created_at
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/graph/nodes.ts
|
|
187
|
+
var NodeStore = class {
|
|
188
|
+
constructor(db) {
|
|
189
|
+
this.db = db;
|
|
190
|
+
}
|
|
191
|
+
db;
|
|
192
|
+
async upsert(node) {
|
|
193
|
+
const stmt = this.db.prepare(`
|
|
194
|
+
INSERT INTO nodes (id, type, content, original, metadata, created_at, accessed_at, access_count)
|
|
195
|
+
VALUES (?, ?, ?, ?, ?, unixepoch(), unixepoch(), 1)
|
|
196
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
197
|
+
content = excluded.content,
|
|
198
|
+
original = excluded.original,
|
|
199
|
+
metadata = excluded.metadata,
|
|
200
|
+
accessed_at = unixepoch(),
|
|
201
|
+
access_count = access_count + 1
|
|
202
|
+
`);
|
|
203
|
+
stmt.run(
|
|
204
|
+
node.id,
|
|
205
|
+
node.type,
|
|
206
|
+
node.content,
|
|
207
|
+
node.original || null,
|
|
208
|
+
JSON.stringify(node.metadata || {})
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
async get(id) {
|
|
212
|
+
this.db.prepare(`
|
|
213
|
+
UPDATE nodes SET accessed_at = unixepoch(), access_count = access_count + 1
|
|
214
|
+
WHERE id = ?
|
|
215
|
+
`).run(id);
|
|
216
|
+
const row = this.db.prepare("SELECT * FROM nodes WHERE id = ?").get(id);
|
|
217
|
+
if (!row) return null;
|
|
218
|
+
return this.hydrate(row);
|
|
219
|
+
}
|
|
220
|
+
async delete(id) {
|
|
221
|
+
this.db.prepare("DELETE FROM edges WHERE source = ? OR target = ?").run(id, id);
|
|
222
|
+
this.db.prepare("DELETE FROM nodes WHERE id = ?").run(id);
|
|
223
|
+
}
|
|
224
|
+
async listByType(type) {
|
|
225
|
+
const rows = this.db.prepare("SELECT * FROM nodes WHERE type = ?").all(type);
|
|
226
|
+
return rows.map((row) => this.hydrate(row));
|
|
227
|
+
}
|
|
228
|
+
hydrate(row) {
|
|
229
|
+
return {
|
|
230
|
+
id: row.id,
|
|
231
|
+
type: row.type,
|
|
232
|
+
content: row.content,
|
|
233
|
+
original: row.original || void 0,
|
|
234
|
+
embedding: row.embedding || void 0,
|
|
235
|
+
metadata: JSON.parse(row.metadata),
|
|
236
|
+
createdAt: row.created_at,
|
|
237
|
+
accessedAt: row.accessed_at,
|
|
238
|
+
accessCount: row.access_count
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/compress/confidence.ts
|
|
244
|
+
function calculateConfidence(original, compressed) {
|
|
245
|
+
if (!original.length) return 0;
|
|
246
|
+
let score = 1;
|
|
247
|
+
const ratio = compressed.length / original.length;
|
|
248
|
+
if (ratio < 0.01) score -= 0.5;
|
|
249
|
+
else if (ratio < 0.05) score -= 0.3;
|
|
250
|
+
if (codeBlocksModified(original, compressed)) score -= 0.4;
|
|
251
|
+
if (isKnownPattern(original)) score += 0.1;
|
|
252
|
+
return Math.max(0, Math.min(1, score));
|
|
253
|
+
}
|
|
254
|
+
function codeBlocksModified(original, compressed) {
|
|
255
|
+
const codeBlockRegex = /```[\s\S]*?```/g;
|
|
256
|
+
const originalBlocks = original.match(codeBlockRegex) || [];
|
|
257
|
+
const compressedBlocks = compressed.match(codeBlockRegex) || [];
|
|
258
|
+
if (originalBlocks.length !== compressedBlocks.length) return true;
|
|
259
|
+
for (let i = 0; i < originalBlocks.length; i++) {
|
|
260
|
+
if (originalBlocks[i] !== compressedBlocks[i]) return true;
|
|
261
|
+
}
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
function isKnownPattern(content) {
|
|
265
|
+
const patterns = [
|
|
266
|
+
/export\s+(default\s+)?(function|class|const|let|var)/,
|
|
267
|
+
/import\s+.*from\s+['"]/
|
|
268
|
+
];
|
|
269
|
+
return patterns.some((p) => p.test(content));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/compress/file-read.ts
|
|
273
|
+
var FileReadCompressor = class {
|
|
274
|
+
async compress(input) {
|
|
275
|
+
const { content, filePath, language } = input;
|
|
276
|
+
const signatures = this.extractSignatures(content, language);
|
|
277
|
+
const exports2 = this.extractExports(content);
|
|
278
|
+
const lines = [
|
|
279
|
+
`// ${filePath} (${content.split("\n").length} lines)`,
|
|
280
|
+
"",
|
|
281
|
+
...signatures,
|
|
282
|
+
"",
|
|
283
|
+
"// Exports:",
|
|
284
|
+
...exports2
|
|
285
|
+
];
|
|
286
|
+
const compressed = lines.join("\n");
|
|
287
|
+
return {
|
|
288
|
+
content: compressed,
|
|
289
|
+
original: content,
|
|
290
|
+
confidence: calculateConfidence(content, compressed),
|
|
291
|
+
metadata: {
|
|
292
|
+
strategy: "signatures",
|
|
293
|
+
originalLines: content.split("\n").length,
|
|
294
|
+
compressedLines: lines.length,
|
|
295
|
+
savings: 1 - compressed.length / content.length
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
async decompress(compressed) {
|
|
300
|
+
return compressed.original;
|
|
301
|
+
}
|
|
302
|
+
extractSignatures(content, language) {
|
|
303
|
+
const signatures = [];
|
|
304
|
+
const lines = content.split("\n");
|
|
305
|
+
for (const line of lines) {
|
|
306
|
+
const trimmed = line.trim();
|
|
307
|
+
if (/^(export\s+)?(async\s+)?function\s+\w+/.test(trimmed)) {
|
|
308
|
+
signatures.push(trimmed.replace(/\{[\s\S]*$/, "{ ... }"));
|
|
309
|
+
}
|
|
310
|
+
if (/^(export\s+)?class\s+\w+/.test(trimmed)) {
|
|
311
|
+
signatures.push(trimmed);
|
|
312
|
+
}
|
|
313
|
+
if (/^(export\s+)?(type|interface)\s+\w+/.test(trimmed)) {
|
|
314
|
+
signatures.push(trimmed);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return signatures;
|
|
318
|
+
}
|
|
319
|
+
extractExports(content) {
|
|
320
|
+
const exports2 = [];
|
|
321
|
+
const exportRegex = /export\s+(default\s+)?(function|class|const|let|var|type|interface)\s+(\w+)/g;
|
|
322
|
+
let match;
|
|
323
|
+
while ((match = exportRegex.exec(content)) !== null) {
|
|
324
|
+
exports2.push(` - ${match[3]} (${match[2]})`);
|
|
325
|
+
}
|
|
326
|
+
return exports2;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/compress/git-diff.ts
|
|
331
|
+
var GitDiffCompressor = class {
|
|
332
|
+
async compress(input) {
|
|
333
|
+
const { diff } = input;
|
|
334
|
+
const hunks = this.parseDiff(diff);
|
|
335
|
+
const condensed = hunks.map((hunk) => ({
|
|
336
|
+
file: hunk.file,
|
|
337
|
+
changes: this.condenseHunk(hunk),
|
|
338
|
+
stats: { additions: hunk.additions, deletions: hunk.deletions }
|
|
339
|
+
}));
|
|
340
|
+
const lines = [];
|
|
341
|
+
for (const hunk of condensed) {
|
|
342
|
+
if (hunk.changes.length > 0) {
|
|
343
|
+
lines.push(`${hunk.file} (+${hunk.stats.additions} -${hunk.stats.deletions})`);
|
|
344
|
+
lines.push(...hunk.changes);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const compressed = lines.join("\n");
|
|
348
|
+
return {
|
|
349
|
+
content: compressed,
|
|
350
|
+
original: diff,
|
|
351
|
+
confidence: calculateConfidence(diff, compressed),
|
|
352
|
+
metadata: {
|
|
353
|
+
strategy: "condensed",
|
|
354
|
+
filesChanged: condensed.length,
|
|
355
|
+
savings: 1 - compressed.length / diff.length
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
async decompress(compressed) {
|
|
360
|
+
return compressed.original;
|
|
361
|
+
}
|
|
362
|
+
parseDiff(diff) {
|
|
363
|
+
const hunks = [];
|
|
364
|
+
const lines = diff.split("\n");
|
|
365
|
+
let currentHunk = null;
|
|
366
|
+
for (const line of lines) {
|
|
367
|
+
if (line.startsWith("diff --git")) {
|
|
368
|
+
if (currentHunk) hunks.push(currentHunk);
|
|
369
|
+
const fileMatch = line.match(/b\/(.+)/);
|
|
370
|
+
currentHunk = {
|
|
371
|
+
file: fileMatch?.[1] || "unknown",
|
|
372
|
+
lines: [],
|
|
373
|
+
additions: 0,
|
|
374
|
+
deletions: 0
|
|
375
|
+
};
|
|
376
|
+
} else if (currentHunk) {
|
|
377
|
+
if (line.startsWith("+")) {
|
|
378
|
+
currentHunk.additions++;
|
|
379
|
+
currentHunk.lines.push({ type: "addition", content: line.slice(1) });
|
|
380
|
+
} else if (line.startsWith("-")) {
|
|
381
|
+
currentHunk.deletions++;
|
|
382
|
+
currentHunk.lines.push({ type: "deletion", content: line.slice(1) });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (currentHunk) hunks.push(currentHunk);
|
|
387
|
+
return hunks;
|
|
388
|
+
}
|
|
389
|
+
condenseHunk(hunk) {
|
|
390
|
+
return hunk.lines.filter((l) => l.type === "addition" || l.type === "deletion").map((l) => `${l.type === "addition" ? "+" : "-"} ${l.content}`);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// src/compress/grep.ts
|
|
395
|
+
var GrepCompressor = class {
|
|
396
|
+
async compress(input) {
|
|
397
|
+
const { results } = input;
|
|
398
|
+
const grouped = this.groupByFile(results);
|
|
399
|
+
const lines = [];
|
|
400
|
+
for (const [file, matches] of grouped) {
|
|
401
|
+
lines.push(`${file} (${matches.length} matches)`);
|
|
402
|
+
const unique = this.deduplicate(matches);
|
|
403
|
+
for (const match of unique) {
|
|
404
|
+
lines.push(` L${match.line}: ${match.content}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const compressed = lines.join("\n");
|
|
408
|
+
const original = results.map((r) => `${r.file}:${r.line}: ${r.content}`).join("\n");
|
|
409
|
+
return {
|
|
410
|
+
content: compressed,
|
|
411
|
+
original,
|
|
412
|
+
confidence: calculateConfidence(original, compressed),
|
|
413
|
+
metadata: {
|
|
414
|
+
strategy: "grouped",
|
|
415
|
+
totalMatches: results.length,
|
|
416
|
+
uniqueFiles: grouped.size,
|
|
417
|
+
savings: 1 - compressed.length / original.length
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async decompress(compressed) {
|
|
422
|
+
return compressed.original;
|
|
423
|
+
}
|
|
424
|
+
groupByFile(results) {
|
|
425
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
426
|
+
for (const result of results) {
|
|
427
|
+
const existing = grouped.get(result.file) || [];
|
|
428
|
+
existing.push(result);
|
|
429
|
+
grouped.set(result.file, existing);
|
|
430
|
+
}
|
|
431
|
+
return grouped;
|
|
432
|
+
}
|
|
433
|
+
deduplicate(matches) {
|
|
434
|
+
const seen = /* @__PURE__ */ new Set();
|
|
435
|
+
return matches.filter((match) => {
|
|
436
|
+
const key = match.content.trim();
|
|
437
|
+
if (seen.has(key)) return false;
|
|
438
|
+
seen.add(key);
|
|
439
|
+
return true;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// src/compress/output.ts
|
|
445
|
+
var OutputCompressor = class {
|
|
446
|
+
static compress(input, options = {}) {
|
|
447
|
+
const { maxLength = 1e3, removeWhitespace = false, prettyPrint = false } = options;
|
|
448
|
+
let output = input;
|
|
449
|
+
if (removeWhitespace) {
|
|
450
|
+
output = output.replace(/\s+/g, " ").trim();
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const parsed = JSON.parse(output);
|
|
454
|
+
if (prettyPrint) {
|
|
455
|
+
output = JSON.stringify(parsed, null, 2);
|
|
456
|
+
} else {
|
|
457
|
+
output = JSON.stringify(parsed);
|
|
458
|
+
}
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
if (output.length > maxLength) {
|
|
462
|
+
const suffix = "...[truncated]";
|
|
463
|
+
output = output.substring(0, maxLength - suffix.length) + suffix;
|
|
464
|
+
}
|
|
465
|
+
return output;
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/cache/l1-session.ts
|
|
470
|
+
var SessionCache = class {
|
|
471
|
+
store = /* @__PURE__ */ new Map();
|
|
472
|
+
hits = 0;
|
|
473
|
+
misses = 0;
|
|
474
|
+
get(key) {
|
|
475
|
+
const entry = this.store.get(key);
|
|
476
|
+
if (entry) {
|
|
477
|
+
this.hits++;
|
|
478
|
+
return entry;
|
|
479
|
+
}
|
|
480
|
+
this.misses++;
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
set(key, value) {
|
|
484
|
+
this.store.set(key, value);
|
|
485
|
+
}
|
|
486
|
+
clear() {
|
|
487
|
+
this.store.clear();
|
|
488
|
+
this.hits = 0;
|
|
489
|
+
this.misses = 0;
|
|
490
|
+
}
|
|
491
|
+
stats() {
|
|
492
|
+
const total = this.hits + this.misses;
|
|
493
|
+
return {
|
|
494
|
+
hits: this.hits,
|
|
495
|
+
misses: this.misses,
|
|
496
|
+
size: this.store.size,
|
|
497
|
+
hitRate: total > 0 ? this.hits / total : 0
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
// src/cache/l2-cross.ts
|
|
503
|
+
var CrossSessionCache = class {
|
|
504
|
+
constructor(db) {
|
|
505
|
+
this.db = db;
|
|
506
|
+
}
|
|
507
|
+
db;
|
|
508
|
+
async get(key) {
|
|
509
|
+
const row = this.db.prepare(`
|
|
510
|
+
SELECT content, metadata, created_at
|
|
511
|
+
FROM nodes
|
|
512
|
+
WHERE id = ? AND type = 'cache'
|
|
513
|
+
`).get(key);
|
|
514
|
+
if (!row) return null;
|
|
515
|
+
const metadata = JSON.parse(row.metadata);
|
|
516
|
+
if (metadata.ttl) {
|
|
517
|
+
const age = Date.now() / 1e3 - row.created_at;
|
|
518
|
+
if (age > metadata.ttl) {
|
|
519
|
+
this.db.prepare("DELETE FROM nodes WHERE id = ?").run(key);
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
this.db.prepare(`
|
|
524
|
+
UPDATE nodes SET accessed_at = unixepoch(), access_count = access_count + 1
|
|
525
|
+
WHERE id = ?
|
|
526
|
+
`).run(key);
|
|
527
|
+
return {
|
|
528
|
+
content: row.content,
|
|
529
|
+
metadata: JSON.parse(row.metadata)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
async set(key, value, options) {
|
|
533
|
+
this.db.prepare(`
|
|
534
|
+
INSERT INTO nodes (id, type, content, metadata, created_at, accessed_at, access_count)
|
|
535
|
+
VALUES (?, 'cache', ?, ?, unixepoch(), unixepoch(), 1)
|
|
536
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
537
|
+
content = excluded.content,
|
|
538
|
+
metadata = excluded.metadata,
|
|
539
|
+
accessed_at = unixepoch(),
|
|
540
|
+
access_count = access_count + 1
|
|
541
|
+
`).run(key, value.content, JSON.stringify({ ...value.metadata, ttl: options?.ttl }));
|
|
542
|
+
}
|
|
543
|
+
async delete(key) {
|
|
544
|
+
this.db.prepare("DELETE FROM nodes WHERE id = ?").run(key);
|
|
545
|
+
}
|
|
546
|
+
async clear() {
|
|
547
|
+
this.db.prepare("DELETE FROM nodes WHERE type = 'cache'").run();
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// src/cache/semantic.ts
|
|
552
|
+
var SemanticCache = class {
|
|
553
|
+
entries = [];
|
|
554
|
+
store(content, embedding, metadata = {}) {
|
|
555
|
+
const entry = {
|
|
556
|
+
id: `sc${this.entries.length + 1}`,
|
|
557
|
+
content,
|
|
558
|
+
embedding,
|
|
559
|
+
metadata,
|
|
560
|
+
createdAt: Date.now()
|
|
561
|
+
};
|
|
562
|
+
this.entries.push(entry);
|
|
563
|
+
return entry;
|
|
564
|
+
}
|
|
565
|
+
search(query, minSimilarity = 0.8) {
|
|
566
|
+
const results = [];
|
|
567
|
+
for (const entry of this.entries) {
|
|
568
|
+
const similarity = this.cosineSimilarity(query, entry.embedding);
|
|
569
|
+
if (similarity >= minSimilarity) {
|
|
570
|
+
results.push({ entry, similarity });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return results.sort((a, b) => b.similarity - a.similarity);
|
|
574
|
+
}
|
|
575
|
+
getAll() {
|
|
576
|
+
return this.entries;
|
|
577
|
+
}
|
|
578
|
+
cosineSimilarity(a, b) {
|
|
579
|
+
if (a.length !== b.length) return 0;
|
|
580
|
+
let dotProduct = 0;
|
|
581
|
+
let normA = 0;
|
|
582
|
+
let normB = 0;
|
|
583
|
+
for (let i = 0; i < a.length; i++) {
|
|
584
|
+
dotProduct += a[i] * b[i];
|
|
585
|
+
normA += a[i] * a[i];
|
|
586
|
+
normB += b[i] * b[i];
|
|
587
|
+
}
|
|
588
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// src/cache/index.ts
|
|
593
|
+
function createCacheManager(db) {
|
|
594
|
+
const session = new SessionCache();
|
|
595
|
+
const crossSession = db ? new CrossSessionCache(db) : null;
|
|
596
|
+
const semantic = new SemanticCache();
|
|
597
|
+
return {
|
|
598
|
+
session,
|
|
599
|
+
crossSession,
|
|
600
|
+
semantic
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function makeLatenCache(db) {
|
|
604
|
+
const l1 = new SessionCache();
|
|
605
|
+
const l2 = new CrossSessionCache(db);
|
|
606
|
+
return {
|
|
607
|
+
l1,
|
|
608
|
+
l2,
|
|
609
|
+
async get(key) {
|
|
610
|
+
const l1Result = l1.get(key);
|
|
611
|
+
if (l1Result) return l1Result;
|
|
612
|
+
const l2Result = await l2.get(key);
|
|
613
|
+
if (l2Result) {
|
|
614
|
+
l1.set(key, l2Result);
|
|
615
|
+
return l2Result;
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
},
|
|
619
|
+
async set(key, value, options) {
|
|
620
|
+
l1.set(key, value);
|
|
621
|
+
await l2.set(key, value, options);
|
|
622
|
+
},
|
|
623
|
+
async invalidate(pattern) {
|
|
624
|
+
l1.clear();
|
|
625
|
+
await l2.clear();
|
|
626
|
+
},
|
|
627
|
+
stats() {
|
|
628
|
+
return {
|
|
629
|
+
l1: l1.stats(),
|
|
630
|
+
l2: { size: 0 }
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/route/tool-router.ts
|
|
637
|
+
var ToolRouter = class {
|
|
638
|
+
rules = [
|
|
639
|
+
{
|
|
640
|
+
matcher: (input) => input.type === "file",
|
|
641
|
+
compressor: "file-read",
|
|
642
|
+
confidence: 0.95,
|
|
643
|
+
reason: "File read detected"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
matcher: (input) => input.type === "grep",
|
|
647
|
+
compressor: "grep",
|
|
648
|
+
confidence: 0.95,
|
|
649
|
+
reason: "Grep results detected"
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
matcher: (input) => input.type === "git-diff",
|
|
653
|
+
compressor: "git-diff",
|
|
654
|
+
confidence: 0.95,
|
|
655
|
+
reason: "Git diff detected"
|
|
656
|
+
}
|
|
657
|
+
];
|
|
658
|
+
route(input) {
|
|
659
|
+
for (const rule of this.rules) {
|
|
660
|
+
if (rule.matcher(input)) {
|
|
661
|
+
return {
|
|
662
|
+
tool: "compress",
|
|
663
|
+
compressor: rule.compressor,
|
|
664
|
+
confidence: rule.confidence,
|
|
665
|
+
reason: rule.reason
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
tool: "compress",
|
|
671
|
+
confidence: 0.1,
|
|
672
|
+
reason: "No matching route found"
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
addRule(rule) {
|
|
676
|
+
this.rules.push(rule);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// src/route/strategy-router.ts
|
|
681
|
+
var StrategyRouter = class {
|
|
682
|
+
thresholds = {
|
|
683
|
+
small: 500,
|
|
684
|
+
large: 5e3
|
|
685
|
+
};
|
|
686
|
+
select(context) {
|
|
687
|
+
if (context.userPreference) {
|
|
688
|
+
return {
|
|
689
|
+
strategy: context.userPreference,
|
|
690
|
+
reason: `User preference: ${context.userPreference}`,
|
|
691
|
+
savings: this.estimateSavings(context.userPreference)
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const fileSize = context.fileSize || 0;
|
|
695
|
+
if (fileSize >= this.thresholds.large) {
|
|
696
|
+
return {
|
|
697
|
+
strategy: "aggressive",
|
|
698
|
+
reason: `large file (${fileSize} bytes)`,
|
|
699
|
+
savings: 0.6
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (fileSize <= this.thresholds.small) {
|
|
703
|
+
return {
|
|
704
|
+
strategy: "conservative",
|
|
705
|
+
reason: `small file (${fileSize} bytes)`,
|
|
706
|
+
savings: 0.2
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
strategy: "balanced",
|
|
711
|
+
reason: `Medium file (${fileSize} bytes)`,
|
|
712
|
+
savings: 0.4
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
estimateSavings(strategy) {
|
|
716
|
+
switch (strategy) {
|
|
717
|
+
case "aggressive":
|
|
718
|
+
return 0.6;
|
|
719
|
+
case "balanced":
|
|
720
|
+
return 0.4;
|
|
721
|
+
case "conservative":
|
|
722
|
+
return 0.2;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// src/route/index.ts
|
|
728
|
+
function createRouter() {
|
|
729
|
+
const tool = new ToolRouter();
|
|
730
|
+
const strategy = new StrategyRouter();
|
|
731
|
+
return {
|
|
732
|
+
tool,
|
|
733
|
+
strategy,
|
|
734
|
+
route: (input) => tool.route(input),
|
|
735
|
+
selectStrategy: (context) => strategy.select(context)
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/tool/registry.ts
|
|
740
|
+
var ToolRegistry = class {
|
|
741
|
+
tools = /* @__PURE__ */ new Map();
|
|
742
|
+
register(name, tool) {
|
|
743
|
+
this.tools.set(name, tool);
|
|
744
|
+
}
|
|
745
|
+
get(name) {
|
|
746
|
+
return this.tools.get(name);
|
|
747
|
+
}
|
|
748
|
+
list() {
|
|
749
|
+
return Array.from(this.tools.values());
|
|
750
|
+
}
|
|
751
|
+
has(name) {
|
|
752
|
+
return this.tools.has(name);
|
|
753
|
+
}
|
|
754
|
+
unregister(name) {
|
|
755
|
+
return this.tools.delete(name);
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/tool/semantic-tool.ts
|
|
760
|
+
var SemanticTool = class {
|
|
761
|
+
name;
|
|
762
|
+
intent;
|
|
763
|
+
inputTypes;
|
|
764
|
+
outputType;
|
|
765
|
+
constructor(name, config) {
|
|
766
|
+
this.name = name;
|
|
767
|
+
this.intent = config.intent;
|
|
768
|
+
this.inputTypes = new Set(config.inputTypes);
|
|
769
|
+
this.outputType = config.outputType;
|
|
770
|
+
}
|
|
771
|
+
supportsInput(inputType) {
|
|
772
|
+
return this.inputTypes.has(inputType);
|
|
773
|
+
}
|
|
774
|
+
describe() {
|
|
775
|
+
return `${this.name}: ${this.intent} (${Array.from(this.inputTypes).join(", ")} \u2192 ${this.outputType})`;
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
// src/tool/index.ts
|
|
780
|
+
function createToolRegistry() {
|
|
781
|
+
const registry = new ToolRegistry();
|
|
782
|
+
registry.register("compress", {
|
|
783
|
+
name: "compress",
|
|
784
|
+
description: "Compress content to reduce tokens",
|
|
785
|
+
execute: async (input) => ({ content: "compressed", confidence: 0.9 })
|
|
786
|
+
});
|
|
787
|
+
registry.register("cache", {
|
|
788
|
+
name: "cache",
|
|
789
|
+
description: "Cache content for reuse",
|
|
790
|
+
execute: async (input) => ({ content: "cached", confidence: 1 })
|
|
791
|
+
});
|
|
792
|
+
registry.register("graph", {
|
|
793
|
+
name: "graph",
|
|
794
|
+
description: "Store in knowledge graph",
|
|
795
|
+
execute: async (input) => ({ stored: true })
|
|
796
|
+
});
|
|
797
|
+
return registry;
|
|
798
|
+
}
|
|
799
|
+
function getSemanticTools() {
|
|
800
|
+
return [
|
|
801
|
+
new SemanticTool("compress", {
|
|
802
|
+
intent: "Reduce token usage",
|
|
803
|
+
inputTypes: ["file", "grep", "git-diff"],
|
|
804
|
+
outputType: "compressed"
|
|
805
|
+
}),
|
|
806
|
+
new SemanticTool("cache", {
|
|
807
|
+
intent: "Store for reuse",
|
|
808
|
+
inputTypes: ["compressed", "original"],
|
|
809
|
+
outputType: "cached"
|
|
810
|
+
}),
|
|
811
|
+
new SemanticTool("graph", {
|
|
812
|
+
intent: "Build knowledge base",
|
|
813
|
+
inputTypes: ["any"],
|
|
814
|
+
outputType: "stored"
|
|
815
|
+
})
|
|
816
|
+
];
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/adapter/claude-code.ts
|
|
820
|
+
var ClaudeCodeAdapter = class {
|
|
821
|
+
name = "claude-code";
|
|
822
|
+
version = "1.0.0";
|
|
823
|
+
type = "hook";
|
|
824
|
+
format(input) {
|
|
825
|
+
const { content, context } = input;
|
|
826
|
+
const formatted = this.formatForClaude(content, context);
|
|
827
|
+
return {
|
|
828
|
+
output: formatted,
|
|
829
|
+
format: "claude",
|
|
830
|
+
metadata: {
|
|
831
|
+
filePath: context?.filePath,
|
|
832
|
+
timestamp: Date.now()
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
parse(output) {
|
|
837
|
+
return { content: output, parsed: true };
|
|
838
|
+
}
|
|
839
|
+
formatForClaude(content, context) {
|
|
840
|
+
const parts = [];
|
|
841
|
+
if (context?.filePath) {
|
|
842
|
+
parts.push(`**File:** \`${context.filePath}\``);
|
|
843
|
+
}
|
|
844
|
+
parts.push(content);
|
|
845
|
+
return parts.join("\n\n");
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
// src/adapter/codex.ts
|
|
850
|
+
var CodexAdapter = class {
|
|
851
|
+
name = "codex";
|
|
852
|
+
version = "1.0.0";
|
|
853
|
+
type = "hook";
|
|
854
|
+
format(input) {
|
|
855
|
+
const { content, context } = input;
|
|
856
|
+
const formatted = this.formatForCodex(content, context);
|
|
857
|
+
return {
|
|
858
|
+
output: formatted,
|
|
859
|
+
format: "codex",
|
|
860
|
+
metadata: {
|
|
861
|
+
timestamp: Date.now()
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
parse(output) {
|
|
866
|
+
try {
|
|
867
|
+
return JSON.parse(output);
|
|
868
|
+
} catch {
|
|
869
|
+
return { content: output };
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
formatForCodex(content, context) {
|
|
873
|
+
if (context?.expectJson) {
|
|
874
|
+
return JSON.stringify({ content, timestamp: Date.now() });
|
|
875
|
+
}
|
|
876
|
+
return content;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// src/adapter/gemini-cli.ts
|
|
881
|
+
var GeminiCliAdapter = class {
|
|
882
|
+
name = "gemini-cli";
|
|
883
|
+
version = "1.0.0";
|
|
884
|
+
type = "hook";
|
|
885
|
+
format(input) {
|
|
886
|
+
const { content, context } = input;
|
|
887
|
+
const formatted = this.formatForGemini(content, context);
|
|
888
|
+
const multimodal = context?.includeImage || false;
|
|
889
|
+
return {
|
|
890
|
+
output: formatted,
|
|
891
|
+
format: "gemini",
|
|
892
|
+
metadata: {
|
|
893
|
+
multimodal,
|
|
894
|
+
imagePath: context?.imagePath,
|
|
895
|
+
timestamp: Date.now()
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
parse(output) {
|
|
900
|
+
return { content: output, parsed: true };
|
|
901
|
+
}
|
|
902
|
+
formatForGemini(content, context) {
|
|
903
|
+
const parts = [content];
|
|
904
|
+
if (context?.includeImage && context?.imagePath) {
|
|
905
|
+
parts.push(`[Image: ${context.imagePath}]`);
|
|
906
|
+
}
|
|
907
|
+
return parts.join("\n");
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
// src/adapter/hook.ts
|
|
912
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
913
|
+
var import_path = __toESM(require("path"));
|
|
914
|
+
var HookAdapter = class {
|
|
915
|
+
name;
|
|
916
|
+
version = "1.0.0";
|
|
917
|
+
type = "hook";
|
|
918
|
+
hooks = {};
|
|
919
|
+
constructor(name) {
|
|
920
|
+
this.name = name;
|
|
921
|
+
}
|
|
922
|
+
format(input) {
|
|
923
|
+
const { content, context } = input;
|
|
924
|
+
const parts = [];
|
|
925
|
+
if (context?.filePath) {
|
|
926
|
+
parts.push(`**File:** \`${context.filePath}\``);
|
|
927
|
+
}
|
|
928
|
+
parts.push(content);
|
|
929
|
+
return {
|
|
930
|
+
output: parts.join("\n\n"),
|
|
931
|
+
format: this.name,
|
|
932
|
+
metadata: { filePath: context?.filePath, timestamp: Date.now() }
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
parse(output) {
|
|
936
|
+
return { content: output, parsed: true };
|
|
937
|
+
}
|
|
938
|
+
setHooks(hooks) {
|
|
939
|
+
this.hooks = hooks;
|
|
940
|
+
}
|
|
941
|
+
async intercept(toolCall) {
|
|
942
|
+
if (this.hooks.preToolUse) {
|
|
943
|
+
return this.hooks.preToolUse(toolCall);
|
|
944
|
+
}
|
|
945
|
+
return toolCall;
|
|
946
|
+
}
|
|
947
|
+
async postProcess(result) {
|
|
948
|
+
if (this.hooks.postToolUse) {
|
|
949
|
+
return this.hooks.postToolUse(result);
|
|
950
|
+
}
|
|
951
|
+
return result;
|
|
952
|
+
}
|
|
953
|
+
async install(agentPath) {
|
|
954
|
+
const hooksDir = import_path.default.join(agentPath, "hooks");
|
|
955
|
+
await import_promises.default.mkdir(hooksDir, { recursive: true });
|
|
956
|
+
const preToolHook = `#!/usr/bin/env node
|
|
957
|
+
const { readFileSync } = require('fs');
|
|
958
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
959
|
+
|
|
960
|
+
if (input.tool === 'Read' || input.tool === 'Bash') {
|
|
961
|
+
process.env.MAKE_LATEN_INTERCEPT = '1';
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
process.stdout.write(JSON.stringify(input));
|
|
965
|
+
`;
|
|
966
|
+
const postToolHook = `#!/usr/bin/env node
|
|
967
|
+
const { readFileSync } = require('fs');
|
|
968
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
969
|
+
|
|
970
|
+
if (input.output && input.output.length > 10000) {
|
|
971
|
+
input.output = input.output.slice(0, 10000) + '\\n... (truncated by make-laten)';
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
process.stdout.write(JSON.stringify(input));
|
|
975
|
+
`;
|
|
976
|
+
await import_promises.default.writeFile(import_path.default.join(hooksDir, "pre-tool-use.js"), preToolHook);
|
|
977
|
+
await import_promises.default.writeFile(import_path.default.join(hooksDir, "post-tool-use.js"), postToolHook);
|
|
978
|
+
}
|
|
979
|
+
async uninstall(agentPath) {
|
|
980
|
+
const hooksDir = import_path.default.join(agentPath, "hooks");
|
|
981
|
+
try {
|
|
982
|
+
await import_promises.default.rm(import_path.default.join(hooksDir, "pre-tool-use.js"), { force: true });
|
|
983
|
+
await import_promises.default.rm(import_path.default.join(hooksDir, "post-tool-use.js"), { force: true });
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
async isInstalled(agentPath) {
|
|
988
|
+
try {
|
|
989
|
+
await import_promises.default.access(import_path.default.join(agentPath, "hooks", "pre-tool-use.js"));
|
|
990
|
+
return true;
|
|
991
|
+
} catch {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
// src/adapter/rules.ts
|
|
998
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
999
|
+
var import_path2 = __toESM(require("path"));
|
|
1000
|
+
|
|
1001
|
+
// src/adapter/types.ts
|
|
1002
|
+
var AGENT_CONFIGS = {
|
|
1003
|
+
"claude-code": { type: "hook" },
|
|
1004
|
+
"codex": { type: "hook" },
|
|
1005
|
+
"gemini-cli": { type: "hook" },
|
|
1006
|
+
"cursor": { type: "rules", rulesFile: ".cursorrules" },
|
|
1007
|
+
"windsurf": { type: "rules", rulesFile: ".windsurfrules" },
|
|
1008
|
+
"cline": { type: "rules", rulesFile: ".clinerules" },
|
|
1009
|
+
"copilot": { type: "rules", rulesFile: ".github/copilot-instructions.md" }
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
// src/adapter/rules.ts
|
|
1013
|
+
var RULES_TEMPLATE = `
|
|
1014
|
+
# make-laten efficiency rules
|
|
1015
|
+
|
|
1016
|
+
## Token Optimization
|
|
1017
|
+
- When reading files, prefer using \`make-laten read <file>\` for compressed output
|
|
1018
|
+
- When searching code, prefer using \`make-laten grep <pattern>\` for grouped results
|
|
1019
|
+
- When checking git status, prefer using \`make-laten git diff\` for condensed output
|
|
1020
|
+
|
|
1021
|
+
## Caching
|
|
1022
|
+
- make-laten automatically caches frequently accessed files
|
|
1023
|
+
- Use \`make-laten cache stats\` to see cache performance
|
|
1024
|
+
- Use \`make-laten cache clear\` to reset cache if needed
|
|
1025
|
+
|
|
1026
|
+
## Web Search
|
|
1027
|
+
- Use \`make-laten search <query>\` for optimized web search
|
|
1028
|
+
- Use \`make-laten fetch <url>\` for compressed web fetch
|
|
1029
|
+
`;
|
|
1030
|
+
var RulesAdapter = class {
|
|
1031
|
+
name;
|
|
1032
|
+
version = "1.0.0";
|
|
1033
|
+
type = "rules";
|
|
1034
|
+
constructor(agentName) {
|
|
1035
|
+
this.name = agentName;
|
|
1036
|
+
}
|
|
1037
|
+
format(input) {
|
|
1038
|
+
return {
|
|
1039
|
+
output: input.content,
|
|
1040
|
+
format: "markdown",
|
|
1041
|
+
metadata: { agent: this.name, timestamp: Date.now() }
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
parse(output) {
|
|
1045
|
+
return { content: output };
|
|
1046
|
+
}
|
|
1047
|
+
async install(agentPath) {
|
|
1048
|
+
const config = AGENT_CONFIGS[this.name];
|
|
1049
|
+
if (!config?.rulesFile) {
|
|
1050
|
+
throw new Error(`No rules file configured for ${this.name}`);
|
|
1051
|
+
}
|
|
1052
|
+
const rulesPath = import_path2.default.join(agentPath, config.rulesFile);
|
|
1053
|
+
const existing = await import_promises2.default.readFile(rulesPath, "utf-8").catch(() => "");
|
|
1054
|
+
if (existing.includes("make-laten")) {
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
await import_promises2.default.mkdir(import_path2.default.dirname(rulesPath), { recursive: true });
|
|
1058
|
+
await import_promises2.default.writeFile(rulesPath, existing + "\n\n" + RULES_TEMPLATE);
|
|
1059
|
+
}
|
|
1060
|
+
async uninstall(agentPath) {
|
|
1061
|
+
const config = AGENT_CONFIGS[this.name];
|
|
1062
|
+
if (!config?.rulesFile) return;
|
|
1063
|
+
const rulesPath = import_path2.default.join(agentPath, config.rulesFile);
|
|
1064
|
+
try {
|
|
1065
|
+
let content = await import_promises2.default.readFile(rulesPath, "utf-8");
|
|
1066
|
+
const marker = "# make-laten efficiency rules";
|
|
1067
|
+
const idx = content.indexOf(marker);
|
|
1068
|
+
if (idx !== -1) {
|
|
1069
|
+
const endIdx = content.indexOf("\n#", idx + marker.length);
|
|
1070
|
+
content = content.slice(0, idx).trim() + "\n" + (endIdx !== -1 ? content.slice(endIdx) : "");
|
|
1071
|
+
await import_promises2.default.writeFile(rulesPath, content.trim() + "\n");
|
|
1072
|
+
}
|
|
1073
|
+
} catch {
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async isInstalled(agentPath) {
|
|
1077
|
+
const config = AGENT_CONFIGS[this.name];
|
|
1078
|
+
if (!config?.rulesFile) return false;
|
|
1079
|
+
try {
|
|
1080
|
+
const content = await import_promises2.default.readFile(import_path2.default.join(agentPath, config.rulesFile), "utf-8");
|
|
1081
|
+
return content.includes("make-laten");
|
|
1082
|
+
} catch {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// src/adapter/detector.ts
|
|
1089
|
+
var import_child_process = require("child_process");
|
|
1090
|
+
var import_util = require("util");
|
|
1091
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
1092
|
+
var import_path3 = __toESM(require("path"));
|
|
1093
|
+
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
1094
|
+
var AgentDetector = class {
|
|
1095
|
+
async detectAgents() {
|
|
1096
|
+
const agents = [];
|
|
1097
|
+
for (const [name, config] of Object.entries(AGENT_CONFIGS)) {
|
|
1098
|
+
const detected = await this.detectAgent(name, config.type);
|
|
1099
|
+
if (detected) {
|
|
1100
|
+
agents.push(detected);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return agents;
|
|
1104
|
+
}
|
|
1105
|
+
async detectAgent(name, type) {
|
|
1106
|
+
const commands = {
|
|
1107
|
+
"claude-code": "claude --version",
|
|
1108
|
+
"codex": "codex --version",
|
|
1109
|
+
"gemini-cli": "gemini --version"
|
|
1110
|
+
};
|
|
1111
|
+
if (commands[name]) {
|
|
1112
|
+
try {
|
|
1113
|
+
const { stdout } = await execAsync(commands[name], { timeout: 5e3 });
|
|
1114
|
+
const version = stdout.trim().split("\n")[0];
|
|
1115
|
+
const agentPath = await this.getAgentPath(name);
|
|
1116
|
+
return { name, type, path: agentPath, version, detected: true };
|
|
1117
|
+
} catch {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (type === "rules") {
|
|
1122
|
+
const rulesPath = AGENT_CONFIGS[name]?.rulesFile;
|
|
1123
|
+
if (rulesPath) {
|
|
1124
|
+
try {
|
|
1125
|
+
await import_promises3.default.access(import_path3.default.join(process.cwd(), rulesPath));
|
|
1126
|
+
return { name, type, path: process.cwd(), version: null, detected: true };
|
|
1127
|
+
} catch {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1134
|
+
async getAgentPath(name) {
|
|
1135
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
1136
|
+
const paths = {
|
|
1137
|
+
"claude-code": import_path3.default.join(home, ".claude"),
|
|
1138
|
+
"codex": import_path3.default.join(home, ".codex"),
|
|
1139
|
+
"gemini-cli": import_path3.default.join(home, ".gemini")
|
|
1140
|
+
};
|
|
1141
|
+
return paths[name] || import_path3.default.join(home, `.${name}`);
|
|
1142
|
+
}
|
|
1143
|
+
async hasCommand(cmd) {
|
|
1144
|
+
try {
|
|
1145
|
+
await execAsync(`which ${cmd}`, { timeout: 3e3 });
|
|
1146
|
+
return true;
|
|
1147
|
+
} catch {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
// src/adapter/installer.ts
|
|
1154
|
+
var Installer = class {
|
|
1155
|
+
detector = new AgentDetector();
|
|
1156
|
+
async install() {
|
|
1157
|
+
const agents = await this.detector.detectAgents();
|
|
1158
|
+
const installed = [];
|
|
1159
|
+
const skipped = [];
|
|
1160
|
+
for (const agent of agents) {
|
|
1161
|
+
try {
|
|
1162
|
+
const adapter = this.createAdapter(agent);
|
|
1163
|
+
if (adapter.install) {
|
|
1164
|
+
await adapter.install(agent.path);
|
|
1165
|
+
installed.push(agent.name);
|
|
1166
|
+
} else {
|
|
1167
|
+
skipped.push(agent.name);
|
|
1168
|
+
}
|
|
1169
|
+
} catch {
|
|
1170
|
+
skipped.push(agent.name);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
return { installed, skipped };
|
|
1174
|
+
}
|
|
1175
|
+
async uninstall() {
|
|
1176
|
+
const agents = await this.detector.detectAgents();
|
|
1177
|
+
const removed = [];
|
|
1178
|
+
for (const agent of agents) {
|
|
1179
|
+
try {
|
|
1180
|
+
const adapter = this.createAdapter(agent);
|
|
1181
|
+
if (adapter.uninstall) {
|
|
1182
|
+
await adapter.uninstall(agent.path);
|
|
1183
|
+
removed.push(agent.name);
|
|
1184
|
+
}
|
|
1185
|
+
} catch {
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return { removed };
|
|
1189
|
+
}
|
|
1190
|
+
async status() {
|
|
1191
|
+
const agents = await this.detector.detectAgents();
|
|
1192
|
+
for (const agent of agents) {
|
|
1193
|
+
const adapter = this.createAdapter(agent);
|
|
1194
|
+
if (adapter.isInstalled) {
|
|
1195
|
+
agent.detected = await adapter.isInstalled(agent.path);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
return agents;
|
|
1199
|
+
}
|
|
1200
|
+
createAdapter(agent) {
|
|
1201
|
+
const config = AGENT_CONFIGS[agent.name];
|
|
1202
|
+
if (config?.type === "rules") {
|
|
1203
|
+
return new RulesAdapter(agent.name);
|
|
1204
|
+
}
|
|
1205
|
+
return new HookAdapter(agent.name);
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1209
|
+
// src/adapter/index.ts
|
|
1210
|
+
var adapters = /* @__PURE__ */ new Map();
|
|
1211
|
+
adapters.set("claude-code", new ClaudeCodeAdapter());
|
|
1212
|
+
adapters.set("codex", new CodexAdapter());
|
|
1213
|
+
adapters.set("gemini-cli", new GeminiCliAdapter());
|
|
1214
|
+
function getAdapter(name) {
|
|
1215
|
+
return adapters.get(name);
|
|
1216
|
+
}
|
|
1217
|
+
function getAdapters() {
|
|
1218
|
+
return Array.from(adapters.values());
|
|
1219
|
+
}
|
|
1220
|
+
function createAdapter(config) {
|
|
1221
|
+
return config;
|
|
1222
|
+
}
|
|
1223
|
+
function registerAdapter(name, adapter) {
|
|
1224
|
+
adapters.set(name, adapter);
|
|
1225
|
+
}
|
|
1226
|
+
function createHookAdapter(name) {
|
|
1227
|
+
return new HookAdapter(name);
|
|
1228
|
+
}
|
|
1229
|
+
function createRulesAdapter(name) {
|
|
1230
|
+
return new RulesAdapter(name);
|
|
1231
|
+
}
|
|
1232
|
+
function createInstaller() {
|
|
1233
|
+
return new Installer();
|
|
1234
|
+
}
|
|
1235
|
+
function createDetector() {
|
|
1236
|
+
return new AgentDetector();
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/learn/pattern-miner.ts
|
|
1240
|
+
var PatternMiner = class {
|
|
1241
|
+
operations = [];
|
|
1242
|
+
patterns = /* @__PURE__ */ new Map();
|
|
1243
|
+
record(operation) {
|
|
1244
|
+
this.operations.push(operation);
|
|
1245
|
+
if (operation.success) {
|
|
1246
|
+
this.updatePatterns(operation);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
getPatterns() {
|
|
1250
|
+
return Array.from(this.patterns.values());
|
|
1251
|
+
}
|
|
1252
|
+
getPatternsByType(type) {
|
|
1253
|
+
return this.getPatterns().filter((p) => p.type === type);
|
|
1254
|
+
}
|
|
1255
|
+
updatePatterns(operation) {
|
|
1256
|
+
const key = operation.type;
|
|
1257
|
+
const existing = this.patterns.get(key);
|
|
1258
|
+
if (existing) {
|
|
1259
|
+
existing.count++;
|
|
1260
|
+
existing.confidence = Math.min(0.99, existing.confidence + 0.05);
|
|
1261
|
+
} else {
|
|
1262
|
+
this.patterns.set(key, {
|
|
1263
|
+
id: `p${this.patterns.size + 1}`,
|
|
1264
|
+
type: operation.type,
|
|
1265
|
+
pattern: JSON.stringify(operation.input),
|
|
1266
|
+
confidence: 0.5,
|
|
1267
|
+
count: 1
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
|
|
1273
|
+
// src/learn/failure-learner.ts
|
|
1274
|
+
var FailureLearner = class {
|
|
1275
|
+
failures = /* @__PURE__ */ new Map();
|
|
1276
|
+
record(failure) {
|
|
1277
|
+
const key = `${failure.type}:${failure.error}`;
|
|
1278
|
+
const existing = this.failures.get(key);
|
|
1279
|
+
if (existing) {
|
|
1280
|
+
existing.count++;
|
|
1281
|
+
} else {
|
|
1282
|
+
this.failures.set(key, {
|
|
1283
|
+
id: `f${this.failures.size + 1}`,
|
|
1284
|
+
...failure,
|
|
1285
|
+
timestamp: Date.now(),
|
|
1286
|
+
count: 1
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
getFailures() {
|
|
1291
|
+
return Array.from(this.failures.values());
|
|
1292
|
+
}
|
|
1293
|
+
getFailuresByType(type) {
|
|
1294
|
+
return this.getFailures().filter((f) => f.type === type);
|
|
1295
|
+
}
|
|
1296
|
+
getSuggestions(type) {
|
|
1297
|
+
const failures = Array.from(this.failures.values()).filter((f) => f.type === type);
|
|
1298
|
+
const suggestions = [];
|
|
1299
|
+
for (const failure of failures) {
|
|
1300
|
+
if (failure.error.includes("not found")) {
|
|
1301
|
+
suggestions.push("check if file exists before processing");
|
|
1302
|
+
}
|
|
1303
|
+
if (failure.error.includes("permission")) {
|
|
1304
|
+
suggestions.push("verify file permissions");
|
|
1305
|
+
}
|
|
1306
|
+
if (failure.count > 2) {
|
|
1307
|
+
suggestions.push(`recurring issue: ${failure.error}`);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return [...new Set(suggestions)];
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
|
|
1314
|
+
// src/learn/index.ts
|
|
1315
|
+
function createLearner() {
|
|
1316
|
+
const patterns = new PatternMiner();
|
|
1317
|
+
const failures = new FailureLearner();
|
|
1318
|
+
return {
|
|
1319
|
+
patterns,
|
|
1320
|
+
failures,
|
|
1321
|
+
record: (op) => {
|
|
1322
|
+
patterns.record(op);
|
|
1323
|
+
if (!op.success) {
|
|
1324
|
+
failures.record({
|
|
1325
|
+
type: op.type,
|
|
1326
|
+
error: op.error || "Unknown error",
|
|
1327
|
+
context: op.input
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// src/correct/auto-correct.ts
|
|
1335
|
+
var AutoCorrect = class {
|
|
1336
|
+
rules = [];
|
|
1337
|
+
corrections = [];
|
|
1338
|
+
appliedCount = 0;
|
|
1339
|
+
addRule(rule) {
|
|
1340
|
+
this.rules.push(rule);
|
|
1341
|
+
}
|
|
1342
|
+
getRules() {
|
|
1343
|
+
return this.rules;
|
|
1344
|
+
}
|
|
1345
|
+
correct(input) {
|
|
1346
|
+
let output = input;
|
|
1347
|
+
for (const rule of this.rules) {
|
|
1348
|
+
if (typeof rule.pattern === "string") {
|
|
1349
|
+
if (output.includes(rule.pattern)) {
|
|
1350
|
+
output = output.split(rule.pattern).join(rule.replacement);
|
|
1351
|
+
this.recordCorrection(input, output, rule.name);
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
const matches = output.match(rule.pattern);
|
|
1355
|
+
if (matches) {
|
|
1356
|
+
output = output.replace(rule.pattern, rule.replacement);
|
|
1357
|
+
this.recordCorrection(input, output, rule.name);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return output;
|
|
1362
|
+
}
|
|
1363
|
+
getStats() {
|
|
1364
|
+
return {
|
|
1365
|
+
applied: this.appliedCount,
|
|
1366
|
+
corrections: this.corrections
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
recordCorrection(original, corrected, ruleName) {
|
|
1370
|
+
this.appliedCount++;
|
|
1371
|
+
this.corrections.push({
|
|
1372
|
+
id: `c${this.corrections.length + 1}`,
|
|
1373
|
+
original,
|
|
1374
|
+
corrected,
|
|
1375
|
+
rule: ruleName,
|
|
1376
|
+
confidence: 0.9
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
// src/correct/index.ts
|
|
1382
|
+
var defaultRules = [
|
|
1383
|
+
{
|
|
1384
|
+
name: "fix-typo-teh",
|
|
1385
|
+
description: 'Fix "teh" typo',
|
|
1386
|
+
pattern: "teh",
|
|
1387
|
+
replacement: "the"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
name: "fix-typo-recieve",
|
|
1391
|
+
description: 'Fix "recieve" typo',
|
|
1392
|
+
pattern: "recieve",
|
|
1393
|
+
replacement: "receive"
|
|
1394
|
+
}
|
|
1395
|
+
];
|
|
1396
|
+
function createCorrector(rules = defaultRules) {
|
|
1397
|
+
const engine = new AutoCorrect();
|
|
1398
|
+
for (const rule of rules) {
|
|
1399
|
+
engine.addRule(rule);
|
|
1400
|
+
}
|
|
1401
|
+
return {
|
|
1402
|
+
engine,
|
|
1403
|
+
rules: engine.getRules(),
|
|
1404
|
+
correct: (input) => engine.correct(input)
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// src/middleware/pipeline.ts
|
|
1409
|
+
var Pipeline = class {
|
|
1410
|
+
middlewares = [];
|
|
1411
|
+
use(middleware) {
|
|
1412
|
+
this.middlewares.push(middleware);
|
|
1413
|
+
return this;
|
|
1414
|
+
}
|
|
1415
|
+
size() {
|
|
1416
|
+
return this.middlewares.length;
|
|
1417
|
+
}
|
|
1418
|
+
async execute(ctx) {
|
|
1419
|
+
let index = 0;
|
|
1420
|
+
const next = async (ctx2) => {
|
|
1421
|
+
if (index >= this.middlewares.length) {
|
|
1422
|
+
return { output: ctx2.input, metadata: ctx2.metadata };
|
|
1423
|
+
}
|
|
1424
|
+
const middleware = this.middlewares[index++];
|
|
1425
|
+
return middleware(ctx2, next);
|
|
1426
|
+
};
|
|
1427
|
+
return next(ctx);
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
// src/plugin/loader.ts
|
|
1432
|
+
var PluginLoader = class {
|
|
1433
|
+
plugins = /* @__PURE__ */ new Map();
|
|
1434
|
+
register(plugin) {
|
|
1435
|
+
this.plugins.set(plugin.name, plugin);
|
|
1436
|
+
}
|
|
1437
|
+
get(name) {
|
|
1438
|
+
return this.plugins.get(name);
|
|
1439
|
+
}
|
|
1440
|
+
has(name) {
|
|
1441
|
+
return this.plugins.has(name);
|
|
1442
|
+
}
|
|
1443
|
+
getAll() {
|
|
1444
|
+
return Array.from(this.plugins.values());
|
|
1445
|
+
}
|
|
1446
|
+
async initializeAll(ctx) {
|
|
1447
|
+
for (const plugin of this.plugins.values()) {
|
|
1448
|
+
await plugin.initialize(ctx);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
async destroyAll() {
|
|
1452
|
+
for (const plugin of this.plugins.values()) {
|
|
1453
|
+
if (plugin.destroy) {
|
|
1454
|
+
await plugin.destroy();
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
// src/security/rate-limiter.ts
|
|
1461
|
+
var RateLimiter = class {
|
|
1462
|
+
config;
|
|
1463
|
+
records = /* @__PURE__ */ new Map();
|
|
1464
|
+
constructor(config) {
|
|
1465
|
+
this.config = config;
|
|
1466
|
+
}
|
|
1467
|
+
allow(key) {
|
|
1468
|
+
const now = Date.now();
|
|
1469
|
+
const record = this.records.get(key);
|
|
1470
|
+
if (!record || now > record.resetAt) {
|
|
1471
|
+
this.records.set(key, { count: 1, resetAt: now + this.config.windowMs });
|
|
1472
|
+
return true;
|
|
1473
|
+
}
|
|
1474
|
+
if (record.count >= this.config.maxRequests) {
|
|
1475
|
+
return false;
|
|
1476
|
+
}
|
|
1477
|
+
record.count++;
|
|
1478
|
+
return true;
|
|
1479
|
+
}
|
|
1480
|
+
getUsage(key) {
|
|
1481
|
+
const record = this.records.get(key);
|
|
1482
|
+
if (!record || Date.now() > record.resetAt) {
|
|
1483
|
+
return 0;
|
|
1484
|
+
}
|
|
1485
|
+
return record.count;
|
|
1486
|
+
}
|
|
1487
|
+
reset(key) {
|
|
1488
|
+
this.records.delete(key);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// src/logging/logger.ts
|
|
1493
|
+
var LOG_LEVELS = {
|
|
1494
|
+
debug: 0,
|
|
1495
|
+
info: 1,
|
|
1496
|
+
warn: 2,
|
|
1497
|
+
error: 3
|
|
1498
|
+
};
|
|
1499
|
+
var Logger = class {
|
|
1500
|
+
config;
|
|
1501
|
+
constructor(config) {
|
|
1502
|
+
this.config = config;
|
|
1503
|
+
}
|
|
1504
|
+
debug(message, metadata) {
|
|
1505
|
+
this.log("debug", message, metadata);
|
|
1506
|
+
}
|
|
1507
|
+
info(message, metadata) {
|
|
1508
|
+
this.log("info", message, metadata);
|
|
1509
|
+
}
|
|
1510
|
+
warn(message, metadata) {
|
|
1511
|
+
this.log("warn", message, metadata);
|
|
1512
|
+
}
|
|
1513
|
+
error(message, metadata) {
|
|
1514
|
+
this.log("error", message, metadata);
|
|
1515
|
+
}
|
|
1516
|
+
log(level, message, metadata) {
|
|
1517
|
+
if (LOG_LEVELS[level] >= LOG_LEVELS[this.config.level]) {
|
|
1518
|
+
this.config.handler({
|
|
1519
|
+
level,
|
|
1520
|
+
message,
|
|
1521
|
+
metadata,
|
|
1522
|
+
timestamp: Date.now()
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
|
|
1528
|
+
// src/error/handler.ts
|
|
1529
|
+
var ErrorHandler = class {
|
|
1530
|
+
errors = [];
|
|
1531
|
+
handle(error) {
|
|
1532
|
+
this.errors.push(error);
|
|
1533
|
+
const category = this.categorize(error);
|
|
1534
|
+
const suggestions = this.getSuggestions(error);
|
|
1535
|
+
return {
|
|
1536
|
+
handled: true,
|
|
1537
|
+
category,
|
|
1538
|
+
suggestions
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
getStats() {
|
|
1542
|
+
const byCategory = {};
|
|
1543
|
+
for (const error of this.errors) {
|
|
1544
|
+
const cat = this.categorize(error);
|
|
1545
|
+
byCategory[cat] = (byCategory[cat] || 0) + 1;
|
|
1546
|
+
}
|
|
1547
|
+
return { total: this.errors.length, byCategory };
|
|
1548
|
+
}
|
|
1549
|
+
categorize(error) {
|
|
1550
|
+
if (error.message.includes("not found")) return "not-found";
|
|
1551
|
+
if (error.message.includes("permission")) return "permission";
|
|
1552
|
+
if (error.message.includes("timeout")) return "timeout";
|
|
1553
|
+
return "unknown";
|
|
1554
|
+
}
|
|
1555
|
+
getSuggestions(error) {
|
|
1556
|
+
const suggestions = [];
|
|
1557
|
+
if (error.message.includes("not found")) {
|
|
1558
|
+
suggestions.push("check if file or resource exists");
|
|
1559
|
+
}
|
|
1560
|
+
if (error.message.includes("permission")) {
|
|
1561
|
+
suggestions.push("verify access permissions");
|
|
1562
|
+
}
|
|
1563
|
+
if (error.message.includes("timeout")) {
|
|
1564
|
+
suggestions.push("increase timeout or retry");
|
|
1565
|
+
}
|
|
1566
|
+
return suggestions;
|
|
1567
|
+
}
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
// src/metrics/collector.ts
|
|
1571
|
+
var MetricsCollector = class {
|
|
1572
|
+
counters = /* @__PURE__ */ new Map();
|
|
1573
|
+
gauges = /* @__PURE__ */ new Map();
|
|
1574
|
+
histograms = /* @__PURE__ */ new Map();
|
|
1575
|
+
increment(name, value = 1) {
|
|
1576
|
+
this.counters.set(name, (this.counters.get(name) || 0) + value);
|
|
1577
|
+
}
|
|
1578
|
+
getCounter(name) {
|
|
1579
|
+
return this.counters.get(name) || 0;
|
|
1580
|
+
}
|
|
1581
|
+
setGauge(name, value) {
|
|
1582
|
+
this.gauges.set(name, value);
|
|
1583
|
+
}
|
|
1584
|
+
getGauge(name) {
|
|
1585
|
+
return this.gauges.get(name) || 0;
|
|
1586
|
+
}
|
|
1587
|
+
record(name, value) {
|
|
1588
|
+
const values = this.histograms.get(name) || [];
|
|
1589
|
+
values.push(value);
|
|
1590
|
+
this.histograms.set(name, values);
|
|
1591
|
+
}
|
|
1592
|
+
getHistogram(name) {
|
|
1593
|
+
const values = this.histograms.get(name) || [];
|
|
1594
|
+
if (values.length === 0) {
|
|
1595
|
+
return { avg: 0, min: 0, max: 0, count: 0 };
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
avg: values.reduce((a, b) => a + b, 0) / values.length,
|
|
1599
|
+
min: Math.min(...values),
|
|
1600
|
+
max: Math.max(...values),
|
|
1601
|
+
count: values.length
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
reset() {
|
|
1605
|
+
this.counters.clear();
|
|
1606
|
+
this.gauges.clear();
|
|
1607
|
+
this.histograms.clear();
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
|
|
1611
|
+
// src/session/manager.ts
|
|
1612
|
+
var SessionManager = class {
|
|
1613
|
+
config;
|
|
1614
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1615
|
+
constructor(config) {
|
|
1616
|
+
this.config = config;
|
|
1617
|
+
}
|
|
1618
|
+
create(userId) {
|
|
1619
|
+
const session = {
|
|
1620
|
+
id: `s${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1621
|
+
userId,
|
|
1622
|
+
createdAt: Date.now(),
|
|
1623
|
+
lastAccessedAt: Date.now()
|
|
1624
|
+
};
|
|
1625
|
+
this.sessions.set(session.id, session);
|
|
1626
|
+
return session;
|
|
1627
|
+
}
|
|
1628
|
+
get(id) {
|
|
1629
|
+
const session = this.sessions.get(id);
|
|
1630
|
+
if (session && Date.now() - session.lastAccessedAt > this.config.timeoutMs) {
|
|
1631
|
+
this.sessions.delete(id);
|
|
1632
|
+
return void 0;
|
|
1633
|
+
}
|
|
1634
|
+
if (session) {
|
|
1635
|
+
session.lastAccessedAt = Date.now();
|
|
1636
|
+
}
|
|
1637
|
+
return session;
|
|
1638
|
+
}
|
|
1639
|
+
destroy(id) {
|
|
1640
|
+
this.sessions.delete(id);
|
|
1641
|
+
}
|
|
1642
|
+
getActive() {
|
|
1643
|
+
const now = Date.now();
|
|
1644
|
+
return Array.from(this.sessions.values()).filter(
|
|
1645
|
+
(s) => now - s.lastAccessedAt <= this.config.timeoutMs
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
};
|
|
1649
|
+
|
|
1650
|
+
// src/web/backends/duckduckgo.ts
|
|
1651
|
+
var DuckDuckGoBackend = class {
|
|
1652
|
+
name = "duckduckgo";
|
|
1653
|
+
isAvailable() {
|
|
1654
|
+
return true;
|
|
1655
|
+
}
|
|
1656
|
+
async search(query, options) {
|
|
1657
|
+
const maxResults = options?.maxResults || 5;
|
|
1658
|
+
const url = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
1659
|
+
const response = await fetch(url, {
|
|
1660
|
+
headers: {
|
|
1661
|
+
"User-Agent": "Mozilla/5.0 (compatible; make-laten/0.1.0)"
|
|
1662
|
+
}
|
|
1663
|
+
});
|
|
1664
|
+
const html = await response.text();
|
|
1665
|
+
return this.parseResults(html, maxResults);
|
|
1666
|
+
}
|
|
1667
|
+
parseResults(html, maxResults) {
|
|
1668
|
+
const results = [];
|
|
1669
|
+
const resultRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
1670
|
+
const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>([\s\S]*?)<\/a>/gi;
|
|
1671
|
+
let match;
|
|
1672
|
+
const urls = [];
|
|
1673
|
+
const titles = [];
|
|
1674
|
+
while ((match = resultRegex.exec(html)) !== null && urls.length < maxResults) {
|
|
1675
|
+
const rawUrl = match[1];
|
|
1676
|
+
const urlMatch = rawUrl.match(/uddg=([^&]+)/);
|
|
1677
|
+
const url = urlMatch ? decodeURIComponent(urlMatch[1]) : rawUrl;
|
|
1678
|
+
const title = this.stripTags(match[2]).trim();
|
|
1679
|
+
if (url && title) {
|
|
1680
|
+
urls.push(url);
|
|
1681
|
+
titles.push(title);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
const snippets = [];
|
|
1685
|
+
while ((match = snippetRegex.exec(html)) !== null && snippets.length < maxResults) {
|
|
1686
|
+
snippets.push(this.stripTags(match[1]).trim());
|
|
1687
|
+
}
|
|
1688
|
+
for (let i = 0; i < urls.length; i++) {
|
|
1689
|
+
results.push({
|
|
1690
|
+
title: titles[i],
|
|
1691
|
+
url: urls[i],
|
|
1692
|
+
snippet: snippets[i] || "",
|
|
1693
|
+
score: 1 - i * 0.1
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
return results;
|
|
1697
|
+
}
|
|
1698
|
+
stripTags(html) {
|
|
1699
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'").replace(/"/g, '"');
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
|
|
1703
|
+
// src/web/backends/index.ts
|
|
1704
|
+
function selectBackend(preferred) {
|
|
1705
|
+
const backends = [
|
|
1706
|
+
new DuckDuckGoBackend()
|
|
1707
|
+
];
|
|
1708
|
+
if (preferred) {
|
|
1709
|
+
const found = backends.find((b) => b.name === preferred);
|
|
1710
|
+
if (found) return found;
|
|
1711
|
+
}
|
|
1712
|
+
return backends.find((b) => b.isAvailable()) || backends[0];
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// src/web/extractor.ts
|
|
1716
|
+
function stripTags(html) {
|
|
1717
|
+
return html.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
1718
|
+
}
|
|
1719
|
+
function extractTitle(html) {
|
|
1720
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
1721
|
+
if (titleMatch) return stripTags(titleMatch[1]).trim();
|
|
1722
|
+
const h1Match = html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
|
|
1723
|
+
if (h1Match) return stripTags(h1Match[1]).trim();
|
|
1724
|
+
return "Untitled";
|
|
1725
|
+
}
|
|
1726
|
+
function extractSections(html) {
|
|
1727
|
+
const sections = [];
|
|
1728
|
+
const headingRegex = /<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi;
|
|
1729
|
+
let match;
|
|
1730
|
+
while ((match = headingRegex.exec(html)) !== null) {
|
|
1731
|
+
const level = parseInt(match[1]);
|
|
1732
|
+
const heading = stripTags(match[2]).trim();
|
|
1733
|
+
const startPos = match.index + match[0].length;
|
|
1734
|
+
const nextHeading = html.substring(startPos).match(/<h[1-6]/i);
|
|
1735
|
+
const endPos = nextHeading ? startPos + nextHeading.index : html.length;
|
|
1736
|
+
const content = stripTags(html.substring(startPos, endPos)).trim();
|
|
1737
|
+
if (!content) continue;
|
|
1738
|
+
const importance = level <= 2 ? "primary" : level <= 4 ? "secondary" : "tertiary";
|
|
1739
|
+
sections.push({ heading, level, content, importance });
|
|
1740
|
+
}
|
|
1741
|
+
return sections;
|
|
1742
|
+
}
|
|
1743
|
+
function extractCodeExamples(html) {
|
|
1744
|
+
const examples = [];
|
|
1745
|
+
const codeRegex = /<(?:pre|code)[^>]*>[\s\S]*?<(?:code|\/pre)[^>]*>/gi;
|
|
1746
|
+
const blockRegex = /<pre[^>]*><code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code><\/pre>/gi;
|
|
1747
|
+
let match;
|
|
1748
|
+
while ((match = blockRegex.exec(html)) !== null) {
|
|
1749
|
+
const language = match[1];
|
|
1750
|
+
const code = stripTags(match[2]).trim();
|
|
1751
|
+
if (code.length > 10) {
|
|
1752
|
+
examples.push({ language, code });
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
const inlineRegex = /<code[^>]*class="[^"]*language-(\w+)[^"]*"[^>]*>([\s\S]*?)<\/code>/gi;
|
|
1756
|
+
while ((match = inlineRegex.exec(html)) !== null) {
|
|
1757
|
+
const language = match[1];
|
|
1758
|
+
const code = stripTags(match[2]).trim();
|
|
1759
|
+
if (code.length > 20 && !examples.some((e) => e.code === code)) {
|
|
1760
|
+
examples.push({ language, code });
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return examples.slice(0, 10);
|
|
1764
|
+
}
|
|
1765
|
+
function extractKeyPoints(sections) {
|
|
1766
|
+
const keyPoints = [];
|
|
1767
|
+
for (const section of sections.filter((s) => s.importance === "primary")) {
|
|
1768
|
+
const sentences = section.content.split(/[.!?\n]+/).filter((s) => s.trim().length > 10);
|
|
1769
|
+
if (sentences.length > 0) {
|
|
1770
|
+
keyPoints.push(sentences[0].trim());
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
return keyPoints.slice(0, 5);
|
|
1774
|
+
}
|
|
1775
|
+
function classifyPurpose(html, sections) {
|
|
1776
|
+
const text = (html + " " + sections.map((s) => s.heading).join(" ")).toLowerCase();
|
|
1777
|
+
if (text.includes("api reference") || text.includes("endpoint") || text.includes("parameters")) return "reference";
|
|
1778
|
+
if (text.includes("tutorial") || text.includes("step by step") || text.includes("getting started")) return "tutorial";
|
|
1779
|
+
if (text.includes("blog") || text.includes("published") || text.includes("author")) return "blog";
|
|
1780
|
+
if (text.includes("install") || text.includes("quickstart") || text.includes("overview")) return "documentation";
|
|
1781
|
+
return "documentation";
|
|
1782
|
+
}
|
|
1783
|
+
function extractSemantic(html, url) {
|
|
1784
|
+
const title = extractTitle(html);
|
|
1785
|
+
const sections = extractSections(html);
|
|
1786
|
+
const codeExamples = extractCodeExamples(html);
|
|
1787
|
+
const keyPoints = extractKeyPoints(sections);
|
|
1788
|
+
const purpose = classifyPurpose(html, sections);
|
|
1789
|
+
return {
|
|
1790
|
+
type: "webpage",
|
|
1791
|
+
url,
|
|
1792
|
+
title,
|
|
1793
|
+
purpose,
|
|
1794
|
+
sections,
|
|
1795
|
+
keyPoints,
|
|
1796
|
+
codeExamples,
|
|
1797
|
+
metadata: {
|
|
1798
|
+
language: html.match(/lang="(\w+)"/)?.[1] || "en",
|
|
1799
|
+
author: html.match(/<meta[^>]*name="author"[^>]*content="([^"]+)"/i)?.[1],
|
|
1800
|
+
tags: html.match(/<meta[^>]*name="keywords"[^>]*content="([^"]+)"/i)?.[1]?.split(",").map((t) => t.trim())
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// src/web/compressor.ts
|
|
1806
|
+
function compressWebContent(semantic, options = {}) {
|
|
1807
|
+
const { maxTokens = 1e3, includeCode = true, includeSections = "all" } = options;
|
|
1808
|
+
const lines = [];
|
|
1809
|
+
lines.push(`# ${semantic.title}`);
|
|
1810
|
+
lines.push(`URL: ${semantic.url}`);
|
|
1811
|
+
lines.push(`Purpose: ${semantic.purpose}`);
|
|
1812
|
+
lines.push("");
|
|
1813
|
+
if (semantic.keyPoints.length > 0) {
|
|
1814
|
+
lines.push("## Key Points");
|
|
1815
|
+
for (const point of semantic.keyPoints) {
|
|
1816
|
+
lines.push(`- ${point}`);
|
|
1817
|
+
}
|
|
1818
|
+
lines.push("");
|
|
1819
|
+
}
|
|
1820
|
+
const filteredSections = filterSections(semantic.sections, includeSections);
|
|
1821
|
+
for (const section of filteredSections) {
|
|
1822
|
+
const content = truncateContent(section.content, 500);
|
|
1823
|
+
lines.push(`## ${section.heading}`);
|
|
1824
|
+
lines.push(content);
|
|
1825
|
+
lines.push("");
|
|
1826
|
+
}
|
|
1827
|
+
if (includeCode && semantic.codeExamples.length > 0) {
|
|
1828
|
+
const topExamples = semantic.codeExamples.slice(0, 3);
|
|
1829
|
+
lines.push("## Code Examples");
|
|
1830
|
+
for (const example of topExamples) {
|
|
1831
|
+
lines.push(`\`\`\`${example.language}`);
|
|
1832
|
+
lines.push(truncateContent(example.code, 300));
|
|
1833
|
+
lines.push("```");
|
|
1834
|
+
lines.push("");
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
let result = lines.join("\n");
|
|
1838
|
+
const estimatedTokens = estimateTokens(result);
|
|
1839
|
+
if (estimatedTokens > maxTokens) {
|
|
1840
|
+
result = truncateByTokens(result, maxTokens);
|
|
1841
|
+
}
|
|
1842
|
+
return result;
|
|
1843
|
+
}
|
|
1844
|
+
function filterSections(sections, mode) {
|
|
1845
|
+
if (mode === "none") return [];
|
|
1846
|
+
if (mode === "primary") return sections.filter((s) => s.importance === "primary");
|
|
1847
|
+
return sections;
|
|
1848
|
+
}
|
|
1849
|
+
function truncateContent(content, maxLength) {
|
|
1850
|
+
if (content.length <= maxLength) return content;
|
|
1851
|
+
return content.slice(0, maxLength) + "...";
|
|
1852
|
+
}
|
|
1853
|
+
function estimateTokens(text) {
|
|
1854
|
+
return Math.ceil(text.length / 4);
|
|
1855
|
+
}
|
|
1856
|
+
function truncateByTokens(text, maxTokens) {
|
|
1857
|
+
const maxChars = maxTokens * 4;
|
|
1858
|
+
if (text.length <= maxChars) return text;
|
|
1859
|
+
const truncated = text.slice(0, maxChars);
|
|
1860
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
1861
|
+
return truncated.slice(0, lastNewline > 0 ? lastNewline : maxChars) + "\n\n... (truncated)";
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// src/web/router.ts
|
|
1865
|
+
var WebRouter = class {
|
|
1866
|
+
fetchCache = /* @__PURE__ */ new Map();
|
|
1867
|
+
searchCache = /* @__PURE__ */ new Map();
|
|
1868
|
+
cacheTtl;
|
|
1869
|
+
constructor(options) {
|
|
1870
|
+
this.cacheTtl = options?.cacheTtlMs || 60 * 60 * 1e3;
|
|
1871
|
+
}
|
|
1872
|
+
async search(query, options) {
|
|
1873
|
+
const cacheKey = `search:${query}:${JSON.stringify(options || {})}`;
|
|
1874
|
+
const cached = this.searchCache.get(cacheKey);
|
|
1875
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
1876
|
+
return cached;
|
|
1877
|
+
}
|
|
1878
|
+
const backend = selectBackend(options?.backend);
|
|
1879
|
+
const results = await backend.search(query, options);
|
|
1880
|
+
this.searchCache.set(cacheKey, results);
|
|
1881
|
+
this.storeFreshness(cacheKey);
|
|
1882
|
+
return results;
|
|
1883
|
+
}
|
|
1884
|
+
async fetch(url, options) {
|
|
1885
|
+
const cacheKey = `fetch:${url}:${options?.format || "text"}`;
|
|
1886
|
+
if (options?.cache !== false) {
|
|
1887
|
+
const cached = this.fetchCache.get(cacheKey);
|
|
1888
|
+
if (cached && this.isFresh(cacheKey)) {
|
|
1889
|
+
return cached;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
const startTime = Date.now();
|
|
1893
|
+
const response = await fetch(url);
|
|
1894
|
+
const html = await response.text();
|
|
1895
|
+
const originalSize = html.length;
|
|
1896
|
+
let content;
|
|
1897
|
+
let semantic = void 0;
|
|
1898
|
+
if (options?.extract !== false) {
|
|
1899
|
+
semantic = extractSemantic(html, url);
|
|
1900
|
+
content = compressWebContent(semantic, {
|
|
1901
|
+
includeCode: true,
|
|
1902
|
+
includeSections: options?.format === "html" ? "all" : "primary"
|
|
1903
|
+
});
|
|
1904
|
+
} else {
|
|
1905
|
+
content = html;
|
|
1906
|
+
}
|
|
1907
|
+
const compressedSize = content.length;
|
|
1908
|
+
const result = {
|
|
1909
|
+
url,
|
|
1910
|
+
title: semantic?.title || extractSimpleTitle(html),
|
|
1911
|
+
content,
|
|
1912
|
+
semantic,
|
|
1913
|
+
metadata: {
|
|
1914
|
+
fetchTime: Date.now() - startTime,
|
|
1915
|
+
originalSize,
|
|
1916
|
+
compressedSize,
|
|
1917
|
+
savings: 1 - compressedSize / originalSize
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
if (options?.cache !== false) {
|
|
1921
|
+
this.fetchCache.set(cacheKey, result);
|
|
1922
|
+
this.storeFreshness(cacheKey);
|
|
1923
|
+
}
|
|
1924
|
+
return result;
|
|
1925
|
+
}
|
|
1926
|
+
async extract(url) {
|
|
1927
|
+
return this.fetch(url, { extract: true, compress: true });
|
|
1928
|
+
}
|
|
1929
|
+
async summarize(content, options) {
|
|
1930
|
+
const lines = content.split("\n");
|
|
1931
|
+
const summaryLines = [];
|
|
1932
|
+
let tokenCount = 0;
|
|
1933
|
+
const maxTokens = options?.maxTokens || 200;
|
|
1934
|
+
for (const line of lines) {
|
|
1935
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
1936
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
1937
|
+
if (line.startsWith("#") || line.startsWith("-") || line.startsWith("*")) {
|
|
1938
|
+
summaryLines.push(line);
|
|
1939
|
+
tokenCount += lineTokens;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
if (summaryLines.length === 0 && lines.length > 0) {
|
|
1943
|
+
for (const line of lines.slice(0, 5)) {
|
|
1944
|
+
const lineTokens = Math.ceil(line.length / 4);
|
|
1945
|
+
if (tokenCount + lineTokens > maxTokens) break;
|
|
1946
|
+
summaryLines.push(line);
|
|
1947
|
+
tokenCount += lineTokens;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
return summaryLines.join("\n");
|
|
1951
|
+
}
|
|
1952
|
+
clearCache() {
|
|
1953
|
+
this.fetchCache.clear();
|
|
1954
|
+
this.searchCache.clear();
|
|
1955
|
+
}
|
|
1956
|
+
getCacheStats() {
|
|
1957
|
+
return {
|
|
1958
|
+
fetchSize: this.fetchCache.size,
|
|
1959
|
+
searchSize: this.searchCache.size
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
isFresh(key) {
|
|
1963
|
+
const timestamp = this.freshness.get(key);
|
|
1964
|
+
if (!timestamp) return false;
|
|
1965
|
+
return Date.now() - timestamp < this.cacheTtl;
|
|
1966
|
+
}
|
|
1967
|
+
storeFreshness(key) {
|
|
1968
|
+
this.freshness.set(key, Date.now());
|
|
1969
|
+
}
|
|
1970
|
+
freshness = /* @__PURE__ */ new Map();
|
|
1971
|
+
};
|
|
1972
|
+
function extractSimpleTitle(html) {
|
|
1973
|
+
const titleMatch = html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
1974
|
+
return titleMatch ? titleMatch[1].replace(/<[^>]+>/g, "").trim() : "Untitled";
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
// src/index.ts
|
|
1978
|
+
var VERSION = "0.1.0";
|
|
1979
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1980
|
+
0 && (module.exports = {
|
|
1981
|
+
AGENT_CONFIGS,
|
|
1982
|
+
AgentDetector,
|
|
1983
|
+
AutoCorrect,
|
|
1984
|
+
ClaudeCodeAdapter,
|
|
1985
|
+
CodexAdapter,
|
|
1986
|
+
CrossSessionCache,
|
|
1987
|
+
DuckDuckGoBackend,
|
|
1988
|
+
EdgeStore,
|
|
1989
|
+
ErrorHandler,
|
|
1990
|
+
FailureLearner,
|
|
1991
|
+
FileReadCompressor,
|
|
1992
|
+
GeminiCliAdapter,
|
|
1993
|
+
GitDiffCompressor,
|
|
1994
|
+
GrepCompressor,
|
|
1995
|
+
HookAdapter,
|
|
1996
|
+
Installer,
|
|
1997
|
+
Logger,
|
|
1998
|
+
MetricsCollector,
|
|
1999
|
+
NodeStore,
|
|
2000
|
+
OutputCompressor,
|
|
2001
|
+
PatternMiner,
|
|
2002
|
+
Pipeline,
|
|
2003
|
+
PluginLoader,
|
|
2004
|
+
RateLimiter,
|
|
2005
|
+
RulesAdapter,
|
|
2006
|
+
SemanticCache,
|
|
2007
|
+
SemanticTool,
|
|
2008
|
+
SessionCache,
|
|
2009
|
+
SessionManager,
|
|
2010
|
+
StrategyRouter,
|
|
2011
|
+
ToolRegistry,
|
|
2012
|
+
ToolRouter,
|
|
2013
|
+
VERSION,
|
|
2014
|
+
WebRouter,
|
|
2015
|
+
calculateConfidence,
|
|
2016
|
+
compressWebContent,
|
|
2017
|
+
createAdapter,
|
|
2018
|
+
createCacheManager,
|
|
2019
|
+
createCorrector,
|
|
2020
|
+
createDatabase,
|
|
2021
|
+
createDetector,
|
|
2022
|
+
createHookAdapter,
|
|
2023
|
+
createInstaller,
|
|
2024
|
+
createLearner,
|
|
2025
|
+
createRouter,
|
|
2026
|
+
createRulesAdapter,
|
|
2027
|
+
createToolRegistry,
|
|
2028
|
+
extractSemantic,
|
|
2029
|
+
getAdapter,
|
|
2030
|
+
getAdapters,
|
|
2031
|
+
getSemanticTools,
|
|
2032
|
+
makeLatenCache,
|
|
2033
|
+
registerAdapter,
|
|
2034
|
+
selectBackend
|
|
2035
|
+
});
|
|
2036
|
+
//# sourceMappingURL=index.js.map
|