@ulrichc1/sparn 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/LICENSE +21 -0
- package/PRIVACY.md +350 -0
- package/README.md +683 -0
- package/SECURITY.md +247 -0
- package/dist/cli/index.cjs +1897 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1868 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.cjs +948 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +567 -0
- package/dist/index.d.ts +567 -0
- package/dist/index.js +900 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,1868 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
13
|
+
import path from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var init_esm_shims = __esm({
|
|
16
|
+
"node_modules/tsup/assets/esm_shims.js"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/cli/ui/colors.ts
|
|
22
|
+
var colors_exports = {};
|
|
23
|
+
__export(colors_exports, {
|
|
24
|
+
bold: () => bold,
|
|
25
|
+
brainPink: () => brainPink,
|
|
26
|
+
dim: () => dim,
|
|
27
|
+
errorRed: () => errorRed,
|
|
28
|
+
neuralCyan: () => neuralCyan,
|
|
29
|
+
synapseViolet: () => synapseViolet
|
|
30
|
+
});
|
|
31
|
+
import chalk from "chalk";
|
|
32
|
+
var neuralCyan, synapseViolet, errorRed, brainPink, dim, bold;
|
|
33
|
+
var init_colors = __esm({
|
|
34
|
+
"src/cli/ui/colors.ts"() {
|
|
35
|
+
"use strict";
|
|
36
|
+
init_esm_shims();
|
|
37
|
+
neuralCyan = chalk.hex("#00D4AA");
|
|
38
|
+
synapseViolet = chalk.hex("#7B61FF");
|
|
39
|
+
errorRed = chalk.hex("#FF6B6B");
|
|
40
|
+
brainPink = chalk.hex("#FF6B9D");
|
|
41
|
+
dim = chalk.dim;
|
|
42
|
+
bold = chalk.bold;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// src/cli/ui/banner.ts
|
|
47
|
+
function getBanner(version) {
|
|
48
|
+
const versionStr = version ? synapseViolet(`v${version}`) : "";
|
|
49
|
+
return `${neuralCyan(BANNER)}
|
|
50
|
+
${brainPink(TAGLINE)}
|
|
51
|
+
${versionStr ? `${versionStr}
|
|
52
|
+
` : ""}`;
|
|
53
|
+
}
|
|
54
|
+
var BANNER, TAGLINE;
|
|
55
|
+
var init_banner = __esm({
|
|
56
|
+
"src/cli/ui/banner.ts"() {
|
|
57
|
+
"use strict";
|
|
58
|
+
init_esm_shims();
|
|
59
|
+
init_colors();
|
|
60
|
+
BANNER = `
|
|
61
|
+
____ ____ ___ ____ _ __
|
|
62
|
+
/ ___\\/ __ \\/ _ \\ / __ \\/ | / /
|
|
63
|
+
\\__ \\/ /_/ / /_\\ \\/ /_/ / |/ /
|
|
64
|
+
___/ / ____/ __ _/ _, _/ /| /
|
|
65
|
+
/____/_/ /_/ |_/_/ |_/_/ |_/
|
|
66
|
+
`;
|
|
67
|
+
TAGLINE = "\u{1F9E0} Neuroscience-inspired context optimization";
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// src/core/kv-memory.ts
|
|
72
|
+
var kv_memory_exports = {};
|
|
73
|
+
__export(kv_memory_exports, {
|
|
74
|
+
createKVMemory: () => createKVMemory
|
|
75
|
+
});
|
|
76
|
+
import { copyFileSync, existsSync } from "fs";
|
|
77
|
+
import Database from "better-sqlite3";
|
|
78
|
+
function createBackup(dbPath) {
|
|
79
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
80
|
+
const backupPath = `${dbPath}.backup-${timestamp}`;
|
|
81
|
+
try {
|
|
82
|
+
copyFileSync(dbPath, backupPath);
|
|
83
|
+
console.log(`\u2713 Database backed up to: ${backupPath}`);
|
|
84
|
+
return backupPath;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(`Warning: Could not create backup: ${error}`);
|
|
87
|
+
return "";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function createKVMemory(dbPath) {
|
|
91
|
+
let db;
|
|
92
|
+
try {
|
|
93
|
+
db = new Database(dbPath);
|
|
94
|
+
const integrityCheck = db.pragma("quick_check", { simple: true });
|
|
95
|
+
if (integrityCheck !== "ok") {
|
|
96
|
+
console.error("\u26A0 Database corruption detected!");
|
|
97
|
+
if (existsSync(dbPath)) {
|
|
98
|
+
const backupPath = createBackup(dbPath);
|
|
99
|
+
if (backupPath) {
|
|
100
|
+
console.log(`Backup created at: ${backupPath}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
console.log("Attempting database recovery...");
|
|
104
|
+
db.close();
|
|
105
|
+
db = new Database(dbPath);
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error("\u26A0 Database error detected:", error);
|
|
109
|
+
if (existsSync(dbPath)) {
|
|
110
|
+
createBackup(dbPath);
|
|
111
|
+
console.log("Creating new database...");
|
|
112
|
+
}
|
|
113
|
+
db = new Database(dbPath);
|
|
114
|
+
}
|
|
115
|
+
db.pragma("journal_mode = WAL");
|
|
116
|
+
db.exec(`
|
|
117
|
+
CREATE TABLE IF NOT EXISTS entries_index (
|
|
118
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
119
|
+
hash TEXT UNIQUE NOT NULL,
|
|
120
|
+
timestamp INTEGER NOT NULL,
|
|
121
|
+
score REAL NOT NULL DEFAULT 0.0 CHECK(score >= 0.0 AND score <= 1.0),
|
|
122
|
+
ttl INTEGER NOT NULL CHECK(ttl >= 0),
|
|
123
|
+
state TEXT NOT NULL CHECK(state IN ('silent', 'ready', 'active')),
|
|
124
|
+
accessCount INTEGER NOT NULL DEFAULT 0 CHECK(accessCount >= 0),
|
|
125
|
+
isBTSP INTEGER NOT NULL DEFAULT 0 CHECK(isBTSP IN (0, 1)),
|
|
126
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
127
|
+
);
|
|
128
|
+
`);
|
|
129
|
+
db.exec(`
|
|
130
|
+
CREATE TABLE IF NOT EXISTS entries_value (
|
|
131
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
132
|
+
content TEXT NOT NULL,
|
|
133
|
+
tags TEXT,
|
|
134
|
+
metadata TEXT,
|
|
135
|
+
FOREIGN KEY (id) REFERENCES entries_index(id) ON DELETE CASCADE
|
|
136
|
+
);
|
|
137
|
+
`);
|
|
138
|
+
db.exec(`
|
|
139
|
+
CREATE TABLE IF NOT EXISTS optimization_stats (
|
|
140
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
141
|
+
timestamp INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
142
|
+
tokens_before INTEGER NOT NULL,
|
|
143
|
+
tokens_after INTEGER NOT NULL,
|
|
144
|
+
entries_pruned INTEGER NOT NULL,
|
|
145
|
+
duration_ms INTEGER NOT NULL
|
|
146
|
+
);
|
|
147
|
+
`);
|
|
148
|
+
db.exec(`
|
|
149
|
+
CREATE INDEX IF NOT EXISTS idx_entries_state ON entries_index(state);
|
|
150
|
+
CREATE INDEX IF NOT EXISTS idx_entries_score ON entries_index(score DESC);
|
|
151
|
+
CREATE INDEX IF NOT EXISTS idx_entries_hash ON entries_index(hash);
|
|
152
|
+
CREATE INDEX IF NOT EXISTS idx_entries_timestamp ON entries_index(timestamp DESC);
|
|
153
|
+
CREATE INDEX IF NOT EXISTS idx_stats_timestamp ON optimization_stats(timestamp DESC);
|
|
154
|
+
`);
|
|
155
|
+
const putIndexStmt = db.prepare(`
|
|
156
|
+
INSERT OR REPLACE INTO entries_index
|
|
157
|
+
(id, hash, timestamp, score, ttl, state, accessCount, isBTSP)
|
|
158
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
159
|
+
`);
|
|
160
|
+
const putValueStmt = db.prepare(`
|
|
161
|
+
INSERT OR REPLACE INTO entries_value
|
|
162
|
+
(id, content, tags, metadata)
|
|
163
|
+
VALUES (?, ?, ?, ?)
|
|
164
|
+
`);
|
|
165
|
+
const getStmt = db.prepare(`
|
|
166
|
+
SELECT
|
|
167
|
+
i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,
|
|
168
|
+
v.content, v.tags, v.metadata
|
|
169
|
+
FROM entries_index i
|
|
170
|
+
JOIN entries_value v ON i.id = v.id
|
|
171
|
+
WHERE i.id = ?
|
|
172
|
+
`);
|
|
173
|
+
const deleteIndexStmt = db.prepare("DELETE FROM entries_index WHERE id = ?");
|
|
174
|
+
const deleteValueStmt = db.prepare("DELETE FROM entries_value WHERE id = ?");
|
|
175
|
+
return {
|
|
176
|
+
async put(entry) {
|
|
177
|
+
const transaction = db.transaction(() => {
|
|
178
|
+
putIndexStmt.run(
|
|
179
|
+
entry.id,
|
|
180
|
+
entry.hash,
|
|
181
|
+
entry.timestamp,
|
|
182
|
+
entry.score,
|
|
183
|
+
entry.ttl,
|
|
184
|
+
entry.state,
|
|
185
|
+
entry.accessCount,
|
|
186
|
+
entry.isBTSP ? 1 : 0
|
|
187
|
+
);
|
|
188
|
+
putValueStmt.run(
|
|
189
|
+
entry.id,
|
|
190
|
+
entry.content,
|
|
191
|
+
JSON.stringify(entry.tags),
|
|
192
|
+
JSON.stringify(entry.metadata)
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
transaction();
|
|
196
|
+
},
|
|
197
|
+
async get(id) {
|
|
198
|
+
const row = getStmt.get(id);
|
|
199
|
+
if (!row) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const r = row;
|
|
203
|
+
return {
|
|
204
|
+
id: r.id,
|
|
205
|
+
content: r.content,
|
|
206
|
+
hash: r.hash,
|
|
207
|
+
timestamp: r.timestamp,
|
|
208
|
+
score: r.score,
|
|
209
|
+
ttl: r.ttl,
|
|
210
|
+
state: r.state,
|
|
211
|
+
accessCount: r.accessCount,
|
|
212
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
213
|
+
metadata: r.metadata ? JSON.parse(r.metadata) : {},
|
|
214
|
+
isBTSP: r.isBTSP === 1
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
async query(filters) {
|
|
218
|
+
let sql = `
|
|
219
|
+
SELECT
|
|
220
|
+
i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,
|
|
221
|
+
v.content, v.tags, v.metadata
|
|
222
|
+
FROM entries_index i
|
|
223
|
+
JOIN entries_value v ON i.id = v.id
|
|
224
|
+
WHERE 1=1
|
|
225
|
+
`;
|
|
226
|
+
const params = [];
|
|
227
|
+
if (filters.state) {
|
|
228
|
+
sql += " AND i.state = ?";
|
|
229
|
+
params.push(filters.state);
|
|
230
|
+
}
|
|
231
|
+
if (filters.minScore !== void 0) {
|
|
232
|
+
sql += " AND i.score >= ?";
|
|
233
|
+
params.push(filters.minScore);
|
|
234
|
+
}
|
|
235
|
+
if (filters.maxScore !== void 0) {
|
|
236
|
+
sql += " AND i.score <= ?";
|
|
237
|
+
params.push(filters.maxScore);
|
|
238
|
+
}
|
|
239
|
+
if (filters.isBTSP !== void 0) {
|
|
240
|
+
sql += " AND i.isBTSP = ?";
|
|
241
|
+
params.push(filters.isBTSP ? 1 : 0);
|
|
242
|
+
}
|
|
243
|
+
sql += " ORDER BY i.score DESC";
|
|
244
|
+
if (filters.limit) {
|
|
245
|
+
sql += " LIMIT ?";
|
|
246
|
+
params.push(filters.limit);
|
|
247
|
+
}
|
|
248
|
+
if (filters.offset) {
|
|
249
|
+
sql += " OFFSET ?";
|
|
250
|
+
params.push(filters.offset);
|
|
251
|
+
}
|
|
252
|
+
const stmt = db.prepare(sql);
|
|
253
|
+
const rows = stmt.all(...params);
|
|
254
|
+
return rows.map((row) => {
|
|
255
|
+
const r = row;
|
|
256
|
+
return {
|
|
257
|
+
id: r.id,
|
|
258
|
+
content: r.content,
|
|
259
|
+
hash: r.hash,
|
|
260
|
+
timestamp: r.timestamp,
|
|
261
|
+
score: r.score,
|
|
262
|
+
ttl: r.ttl,
|
|
263
|
+
state: r.state,
|
|
264
|
+
accessCount: r.accessCount,
|
|
265
|
+
tags: r.tags ? JSON.parse(r.tags) : [],
|
|
266
|
+
metadata: r.metadata ? JSON.parse(r.metadata) : {},
|
|
267
|
+
isBTSP: r.isBTSP === 1
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
async delete(id) {
|
|
272
|
+
const transaction = db.transaction(() => {
|
|
273
|
+
deleteIndexStmt.run(id);
|
|
274
|
+
deleteValueStmt.run(id);
|
|
275
|
+
});
|
|
276
|
+
transaction();
|
|
277
|
+
},
|
|
278
|
+
async list() {
|
|
279
|
+
const stmt = db.prepare("SELECT id FROM entries_index");
|
|
280
|
+
const rows = stmt.all();
|
|
281
|
+
return rows.map((r) => r.id);
|
|
282
|
+
},
|
|
283
|
+
async compact() {
|
|
284
|
+
const before = db.prepare("SELECT COUNT(*) as count FROM entries_index").get();
|
|
285
|
+
db.exec("DELETE FROM entries_index WHERE ttl <= 0");
|
|
286
|
+
db.exec("VACUUM");
|
|
287
|
+
const after = db.prepare("SELECT COUNT(*) as count FROM entries_index").get();
|
|
288
|
+
return before.count - after.count;
|
|
289
|
+
},
|
|
290
|
+
async close() {
|
|
291
|
+
db.close();
|
|
292
|
+
},
|
|
293
|
+
async recordOptimization(stats) {
|
|
294
|
+
const stmt = db.prepare(`
|
|
295
|
+
INSERT INTO optimization_stats (timestamp, tokens_before, tokens_after, entries_pruned, duration_ms)
|
|
296
|
+
VALUES (?, ?, ?, ?, ?)
|
|
297
|
+
`);
|
|
298
|
+
stmt.run(
|
|
299
|
+
stats.timestamp,
|
|
300
|
+
stats.tokens_before,
|
|
301
|
+
stats.tokens_after,
|
|
302
|
+
stats.entries_pruned,
|
|
303
|
+
stats.duration_ms
|
|
304
|
+
);
|
|
305
|
+
},
|
|
306
|
+
async getOptimizationStats() {
|
|
307
|
+
const stmt = db.prepare(`
|
|
308
|
+
SELECT id, timestamp, tokens_before, tokens_after, entries_pruned, duration_ms
|
|
309
|
+
FROM optimization_stats
|
|
310
|
+
ORDER BY timestamp DESC
|
|
311
|
+
`);
|
|
312
|
+
const rows = stmt.all();
|
|
313
|
+
return rows;
|
|
314
|
+
},
|
|
315
|
+
async clearOptimizationStats() {
|
|
316
|
+
db.exec("DELETE FROM optimization_stats");
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
var init_kv_memory = __esm({
|
|
321
|
+
"src/core/kv-memory.ts"() {
|
|
322
|
+
"use strict";
|
|
323
|
+
init_esm_shims();
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// src/types/config.ts
|
|
328
|
+
var DEFAULT_CONFIG;
|
|
329
|
+
var init_config = __esm({
|
|
330
|
+
"src/types/config.ts"() {
|
|
331
|
+
"use strict";
|
|
332
|
+
init_esm_shims();
|
|
333
|
+
DEFAULT_CONFIG = {
|
|
334
|
+
pruning: {
|
|
335
|
+
threshold: 5,
|
|
336
|
+
aggressiveness: 50
|
|
337
|
+
},
|
|
338
|
+
decay: {
|
|
339
|
+
defaultTTL: 24,
|
|
340
|
+
decayThreshold: 0.95
|
|
341
|
+
},
|
|
342
|
+
states: {
|
|
343
|
+
activeThreshold: 0.7,
|
|
344
|
+
readyThreshold: 0.3
|
|
345
|
+
},
|
|
346
|
+
agent: "generic",
|
|
347
|
+
ui: {
|
|
348
|
+
colors: true,
|
|
349
|
+
sounds: false,
|
|
350
|
+
verbose: false
|
|
351
|
+
},
|
|
352
|
+
autoConsolidate: null
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// src/cli/commands/init.ts
|
|
358
|
+
var init_exports = {};
|
|
359
|
+
__export(init_exports, {
|
|
360
|
+
displayInitSuccess: () => displayInitSuccess,
|
|
361
|
+
initCommand: () => initCommand
|
|
362
|
+
});
|
|
363
|
+
import { readFileSync } from "fs";
|
|
364
|
+
import { access, mkdir, writeFile } from "fs/promises";
|
|
365
|
+
import { dirname, join } from "path";
|
|
366
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
367
|
+
import { dump as dumpYAML } from "js-yaml";
|
|
368
|
+
function getVersion() {
|
|
369
|
+
try {
|
|
370
|
+
const pkg = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
|
|
371
|
+
return pkg.version;
|
|
372
|
+
} catch {
|
|
373
|
+
const __filename2 = fileURLToPath2(import.meta.url);
|
|
374
|
+
const __dirname2 = dirname(__filename2);
|
|
375
|
+
const pkg = JSON.parse(readFileSync(join(__dirname2, "../../../package.json"), "utf-8"));
|
|
376
|
+
return pkg.version;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function initCommand(options = {}) {
|
|
380
|
+
const startTime = Date.now();
|
|
381
|
+
const cwd = options.cwd || process.cwd();
|
|
382
|
+
const sparnDir = join(cwd, ".sparn");
|
|
383
|
+
const configPath = join(sparnDir, "config.yaml");
|
|
384
|
+
const dbPath = join(sparnDir, "memory.db");
|
|
385
|
+
const exists = await checkExists(sparnDir);
|
|
386
|
+
if (exists && !options.force) {
|
|
387
|
+
throw new Error(
|
|
388
|
+
".sparn/ directory already exists. Use --force to overwrite or run from a different directory."
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
await mkdir(sparnDir, { recursive: true });
|
|
392
|
+
const configYAML = dumpYAML(DEFAULT_CONFIG, {
|
|
393
|
+
indent: 2,
|
|
394
|
+
lineWidth: 100
|
|
395
|
+
});
|
|
396
|
+
const configWithComments = `# Sparn Configuration
|
|
397
|
+
# See https://github.com/ulrichc1/sparn for documentation
|
|
398
|
+
|
|
399
|
+
${configYAML}`;
|
|
400
|
+
await writeFile(configPath, configWithComments, "utf8");
|
|
401
|
+
const memory = await createKVMemory(dbPath);
|
|
402
|
+
await memory.close();
|
|
403
|
+
const durationMs = Date.now() - startTime;
|
|
404
|
+
return {
|
|
405
|
+
configPath,
|
|
406
|
+
dbPath,
|
|
407
|
+
durationMs
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function displayInitSuccess(result) {
|
|
411
|
+
console.log(getBanner(VERSION));
|
|
412
|
+
console.log(`
|
|
413
|
+
${brainPink("\u2501".repeat(60))}`);
|
|
414
|
+
console.log(brainPink(" \u{1F9E0} Sparn Initialized Successfully!"));
|
|
415
|
+
console.log(brainPink("\u2501".repeat(60)));
|
|
416
|
+
console.log(`
|
|
417
|
+
${neuralCyan("Config:")} ${dim(result.configPath)}`);
|
|
418
|
+
console.log(` ${neuralCyan("Database:")} ${dim(result.dbPath)}`);
|
|
419
|
+
console.log(` ${neuralCyan("Time:")} ${dim(`${result.durationMs}ms`)}`);
|
|
420
|
+
console.log(
|
|
421
|
+
`
|
|
422
|
+
${brainPink("\u2192")} Run ${neuralCyan("'sparn optimize'")} to start optimizing context!`
|
|
423
|
+
);
|
|
424
|
+
console.log(`${brainPink("\u2501".repeat(60))}
|
|
425
|
+
`);
|
|
426
|
+
}
|
|
427
|
+
async function checkExists(path2) {
|
|
428
|
+
try {
|
|
429
|
+
await access(path2);
|
|
430
|
+
return true;
|
|
431
|
+
} catch {
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
var VERSION;
|
|
436
|
+
var init_init = __esm({
|
|
437
|
+
"src/cli/commands/init.ts"() {
|
|
438
|
+
"use strict";
|
|
439
|
+
init_esm_shims();
|
|
440
|
+
init_kv_memory();
|
|
441
|
+
init_config();
|
|
442
|
+
init_banner();
|
|
443
|
+
init_colors();
|
|
444
|
+
VERSION = getVersion();
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// src/cli/ui/progress.ts
|
|
449
|
+
var progress_exports = {};
|
|
450
|
+
__export(progress_exports, {
|
|
451
|
+
createConsolidateSpinner: () => createConsolidateSpinner,
|
|
452
|
+
createInitSpinner: () => createInitSpinner,
|
|
453
|
+
createOptimizeSpinner: () => createOptimizeSpinner,
|
|
454
|
+
createStatsSpinner: () => createStatsSpinner,
|
|
455
|
+
showConsolidationSummary: () => showConsolidationSummary,
|
|
456
|
+
showInitSuccess: () => showInitSuccess,
|
|
457
|
+
showTokenSavings: () => showTokenSavings
|
|
458
|
+
});
|
|
459
|
+
import ora from "ora";
|
|
460
|
+
function createOptimizeSpinner(text) {
|
|
461
|
+
return ora({
|
|
462
|
+
text,
|
|
463
|
+
color: "cyan",
|
|
464
|
+
spinner: "dots12"
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
function createConsolidateSpinner(text) {
|
|
468
|
+
return ora({
|
|
469
|
+
text,
|
|
470
|
+
color: "magenta",
|
|
471
|
+
spinner: "material"
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
function createStatsSpinner(text) {
|
|
475
|
+
return ora({
|
|
476
|
+
text,
|
|
477
|
+
color: "yellow",
|
|
478
|
+
spinner: "bouncingBar"
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
function createInitSpinner(text) {
|
|
482
|
+
return ora({
|
|
483
|
+
text,
|
|
484
|
+
color: "green",
|
|
485
|
+
spinner: "star"
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function showTokenSavings(tokensBefore, tokensAfter, reduction) {
|
|
489
|
+
const saved = tokensBefore - tokensAfter;
|
|
490
|
+
const reductionPercent = (reduction * 100).toFixed(1);
|
|
491
|
+
console.log(`
|
|
492
|
+
${brainPink("\u2501".repeat(60))}`);
|
|
493
|
+
console.log(neuralCyan(" \u{1F4CA} Token Optimization Results"));
|
|
494
|
+
console.log(brainPink("\u2501".repeat(60)));
|
|
495
|
+
console.log(` ${synapseViolet("Before:")} ${tokensBefore.toLocaleString()} tokens`);
|
|
496
|
+
console.log(` ${neuralCyan("After:")} ${tokensAfter.toLocaleString()} tokens`);
|
|
497
|
+
console.log(brainPink(" \u2193".repeat(30)));
|
|
498
|
+
console.log(` ${brainPink("Saved:")} ${saved.toLocaleString()} tokens (${reductionPercent}%)`);
|
|
499
|
+
const barLength = 40;
|
|
500
|
+
const savedBars = Math.floor(reduction * barLength);
|
|
501
|
+
const keptBars = barLength - savedBars;
|
|
502
|
+
const progressBar = neuralCyan("\u2588".repeat(keptBars)) + brainPink("\u2591".repeat(savedBars));
|
|
503
|
+
console.log(` [${progressBar}] ${reductionPercent}% reduced`);
|
|
504
|
+
if (reduction >= 0.9) {
|
|
505
|
+
console.log(`
|
|
506
|
+
${brainPink("\u2728 OUTSTANDING!")} Mind-blowing 90%+ reduction!`);
|
|
507
|
+
} else if (reduction >= 0.7) {
|
|
508
|
+
console.log(`
|
|
509
|
+
${neuralCyan("\u{1F389} EXCELLENT!")} Strong 70%+ token savings!`);
|
|
510
|
+
} else if (reduction >= 0.5) {
|
|
511
|
+
console.log(`
|
|
512
|
+
${synapseViolet("\u{1F44D} GOOD!")} Solid 50%+ optimization!`);
|
|
513
|
+
} else if (reduction > 0) {
|
|
514
|
+
console.log(`
|
|
515
|
+
${neuralCyan("\u2713")} Tokens optimized successfully!`);
|
|
516
|
+
}
|
|
517
|
+
console.log(`${brainPink("\u2501".repeat(60))}
|
|
518
|
+
`);
|
|
519
|
+
}
|
|
520
|
+
function showConsolidationSummary(entriesBefore, entriesAfter, decayed, duplicates, durationMs) {
|
|
521
|
+
const removed = entriesBefore - entriesAfter;
|
|
522
|
+
const compressionRatio = entriesBefore > 0 ? (removed / entriesBefore * 100).toFixed(1) : "0.0";
|
|
523
|
+
console.log(`
|
|
524
|
+
${brainPink("\u2501".repeat(60))}`);
|
|
525
|
+
console.log(neuralCyan(" \u{1F9F9} Memory Consolidation Complete"));
|
|
526
|
+
console.log(brainPink("\u2501".repeat(60)));
|
|
527
|
+
console.log(` ${synapseViolet("Before:")} ${entriesBefore.toLocaleString()} entries`);
|
|
528
|
+
console.log(` ${neuralCyan("After:")} ${entriesAfter.toLocaleString()} entries`);
|
|
529
|
+
console.log(brainPink(" \u2193".repeat(30)));
|
|
530
|
+
console.log(` ${brainPink("Decayed:")} ${decayed.toLocaleString()} removed`);
|
|
531
|
+
console.log(` ${brainPink("Duplicates:")} ${duplicates.toLocaleString()} merged`);
|
|
532
|
+
console.log(` ${neuralCyan("Total:")} ${removed.toLocaleString()} entries freed`);
|
|
533
|
+
console.log(` ${synapseViolet("Time:")} ${durationMs}ms`);
|
|
534
|
+
const barLength = 40;
|
|
535
|
+
const compressionBars = Math.floor(removed / entriesBefore * barLength);
|
|
536
|
+
const keptBars = barLength - compressionBars;
|
|
537
|
+
const progressBar = neuralCyan("\u2588".repeat(keptBars)) + brainPink("\u2591".repeat(compressionBars));
|
|
538
|
+
console.log(` [${progressBar}] ${compressionRatio}% compressed`);
|
|
539
|
+
if (removed > 0) {
|
|
540
|
+
console.log(`
|
|
541
|
+
${brainPink("\u2728")} Memory optimized and ready for peak performance!`);
|
|
542
|
+
} else {
|
|
543
|
+
console.log(`
|
|
544
|
+
${neuralCyan("\u2713")} Memory already optimal!`);
|
|
545
|
+
}
|
|
546
|
+
console.log(`${brainPink("\u2501".repeat(60))}
|
|
547
|
+
`);
|
|
548
|
+
}
|
|
549
|
+
function showInitSuccess(message) {
|
|
550
|
+
console.log(`
|
|
551
|
+
${brainPink("\u2501".repeat(60))}`);
|
|
552
|
+
console.log(brainPink(" \u{1F9E0} Sparn Initialized Successfully!"));
|
|
553
|
+
console.log(brainPink("\u2501".repeat(60)));
|
|
554
|
+
console.log(` ${neuralCyan(message)}`);
|
|
555
|
+
console.log(`${brainPink("\u2501".repeat(60))}
|
|
556
|
+
`);
|
|
557
|
+
}
|
|
558
|
+
var init_progress = __esm({
|
|
559
|
+
"src/cli/ui/progress.ts"() {
|
|
560
|
+
"use strict";
|
|
561
|
+
init_esm_shims();
|
|
562
|
+
init_colors();
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/utils/hash.ts
|
|
567
|
+
import { createHash } from "crypto";
|
|
568
|
+
function hashContent(content) {
|
|
569
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
570
|
+
}
|
|
571
|
+
var init_hash = __esm({
|
|
572
|
+
"src/utils/hash.ts"() {
|
|
573
|
+
"use strict";
|
|
574
|
+
init_esm_shims();
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
// src/core/btsp-embedder.ts
|
|
579
|
+
import { randomUUID } from "crypto";
|
|
580
|
+
function createBTSPEmbedder() {
|
|
581
|
+
const BTSP_PATTERNS = [
|
|
582
|
+
// Error patterns
|
|
583
|
+
/\b(error|exception|failure|fatal|critical|panic)\b/i,
|
|
584
|
+
/\b(TypeError|ReferenceError|SyntaxError|RangeError|URIError)\b/,
|
|
585
|
+
/\bENOENT|EACCES|ECONNREFUSED|ETIMEDOUT\b/,
|
|
586
|
+
// Stack trace patterns
|
|
587
|
+
/^\s+at\s+.*\(.*:\d+:\d+\)/m,
|
|
588
|
+
// JavaScript stack trace
|
|
589
|
+
/^\s+at\s+.*\.[a-zA-Z]+:\d+/m,
|
|
590
|
+
// Python/Ruby stack trace
|
|
591
|
+
// Git diff new files
|
|
592
|
+
/^new file mode \d+$/m,
|
|
593
|
+
/^--- \/dev\/null$/m,
|
|
594
|
+
// Merge conflict markers
|
|
595
|
+
/^<<<<<<< /m,
|
|
596
|
+
/^=======/m,
|
|
597
|
+
/^>>>>>>> /m
|
|
598
|
+
];
|
|
599
|
+
function detectBTSP(content) {
|
|
600
|
+
return BTSP_PATTERNS.some((pattern) => pattern.test(content));
|
|
601
|
+
}
|
|
602
|
+
function createBTSPEntry(content, tags = [], metadata = {}) {
|
|
603
|
+
return {
|
|
604
|
+
id: randomUUID(),
|
|
605
|
+
content,
|
|
606
|
+
hash: hashContent(content),
|
|
607
|
+
timestamp: Date.now(),
|
|
608
|
+
score: 1,
|
|
609
|
+
// Maximum initial score
|
|
610
|
+
ttl: 365 * 24 * 3600,
|
|
611
|
+
// 1 year in seconds (long retention)
|
|
612
|
+
state: "active",
|
|
613
|
+
// Always active
|
|
614
|
+
accessCount: 0,
|
|
615
|
+
tags: [...tags, "btsp"],
|
|
616
|
+
metadata,
|
|
617
|
+
isBTSP: true
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
detectBTSP,
|
|
622
|
+
createBTSPEntry
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
var init_btsp_embedder = __esm({
|
|
626
|
+
"src/core/btsp-embedder.ts"() {
|
|
627
|
+
"use strict";
|
|
628
|
+
init_esm_shims();
|
|
629
|
+
init_hash();
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// src/core/confidence-states.ts
|
|
634
|
+
function createConfidenceStates(config) {
|
|
635
|
+
const { activeThreshold, readyThreshold } = config;
|
|
636
|
+
function calculateState(entry) {
|
|
637
|
+
if (entry.isBTSP) {
|
|
638
|
+
return "active";
|
|
639
|
+
}
|
|
640
|
+
if (entry.score > activeThreshold) {
|
|
641
|
+
return "active";
|
|
642
|
+
}
|
|
643
|
+
if (entry.score >= readyThreshold) {
|
|
644
|
+
return "ready";
|
|
645
|
+
}
|
|
646
|
+
return "silent";
|
|
647
|
+
}
|
|
648
|
+
function transition(entry) {
|
|
649
|
+
const newState = calculateState(entry);
|
|
650
|
+
return {
|
|
651
|
+
...entry,
|
|
652
|
+
state: newState
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function getDistribution(entries) {
|
|
656
|
+
const distribution = {
|
|
657
|
+
silent: 0,
|
|
658
|
+
ready: 0,
|
|
659
|
+
active: 0,
|
|
660
|
+
total: entries.length
|
|
661
|
+
};
|
|
662
|
+
for (const entry of entries) {
|
|
663
|
+
const state = calculateState(entry);
|
|
664
|
+
distribution[state]++;
|
|
665
|
+
}
|
|
666
|
+
return distribution;
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
calculateState,
|
|
670
|
+
transition,
|
|
671
|
+
getDistribution
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
var init_confidence_states = __esm({
|
|
675
|
+
"src/core/confidence-states.ts"() {
|
|
676
|
+
"use strict";
|
|
677
|
+
init_esm_shims();
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// src/core/engram-scorer.ts
|
|
682
|
+
function createEngramScorer(config) {
|
|
683
|
+
const { defaultTTL } = config;
|
|
684
|
+
function calculateDecay(ageInSeconds, ttlInSeconds) {
|
|
685
|
+
if (ttlInSeconds === 0) return 1;
|
|
686
|
+
if (ageInSeconds <= 0) return 0;
|
|
687
|
+
const ratio = ageInSeconds / ttlInSeconds;
|
|
688
|
+
const decay = 1 - Math.exp(-ratio);
|
|
689
|
+
return Math.max(0, Math.min(1, decay));
|
|
690
|
+
}
|
|
691
|
+
function calculateScore(entry, currentTime = Date.now()) {
|
|
692
|
+
const ageInMilliseconds = currentTime - entry.timestamp;
|
|
693
|
+
const ageInSeconds = Math.max(0, ageInMilliseconds / 1e3);
|
|
694
|
+
const decay = calculateDecay(ageInSeconds, entry.ttl);
|
|
695
|
+
let score = entry.score * (1 - decay);
|
|
696
|
+
if (entry.accessCount > 0) {
|
|
697
|
+
const accessBonus = Math.log(entry.accessCount + 1) * 0.1;
|
|
698
|
+
score = Math.min(1, score + accessBonus);
|
|
699
|
+
}
|
|
700
|
+
if (entry.isBTSP) {
|
|
701
|
+
score = Math.max(score, 0.9);
|
|
702
|
+
}
|
|
703
|
+
return Math.max(0, Math.min(1, score));
|
|
704
|
+
}
|
|
705
|
+
function refreshTTL(entry) {
|
|
706
|
+
return {
|
|
707
|
+
...entry,
|
|
708
|
+
ttl: defaultTTL * 3600,
|
|
709
|
+
// Convert hours to seconds
|
|
710
|
+
timestamp: Date.now()
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
calculateScore,
|
|
715
|
+
refreshTTL,
|
|
716
|
+
calculateDecay
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
var init_engram_scorer = __esm({
|
|
720
|
+
"src/core/engram-scorer.ts"() {
|
|
721
|
+
"use strict";
|
|
722
|
+
init_esm_shims();
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
// src/utils/tokenizer.ts
|
|
727
|
+
function estimateTokens(text) {
|
|
728
|
+
if (!text || text.length === 0) {
|
|
729
|
+
return 0;
|
|
730
|
+
}
|
|
731
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
732
|
+
const wordCount = words.length;
|
|
733
|
+
const charCount = text.length;
|
|
734
|
+
const charEstimate = Math.ceil(charCount / 4);
|
|
735
|
+
const wordEstimate = Math.ceil(wordCount * 0.75);
|
|
736
|
+
return Math.max(wordEstimate, charEstimate);
|
|
737
|
+
}
|
|
738
|
+
var init_tokenizer = __esm({
|
|
739
|
+
"src/utils/tokenizer.ts"() {
|
|
740
|
+
"use strict";
|
|
741
|
+
init_esm_shims();
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
// src/core/sparse-pruner.ts
|
|
746
|
+
function createSparsePruner(config) {
|
|
747
|
+
const { threshold } = config;
|
|
748
|
+
function tokenize(text) {
|
|
749
|
+
return text.toLowerCase().split(/\s+/).filter((word) => word.length > 0);
|
|
750
|
+
}
|
|
751
|
+
function calculateTF(term, tokens) {
|
|
752
|
+
const count = tokens.filter((t) => t === term).length;
|
|
753
|
+
return Math.sqrt(count);
|
|
754
|
+
}
|
|
755
|
+
function calculateIDF(term, allEntries) {
|
|
756
|
+
const totalDocs = allEntries.length;
|
|
757
|
+
const docsWithTerm = allEntries.filter((entry) => {
|
|
758
|
+
const tokens = tokenize(entry.content);
|
|
759
|
+
return tokens.includes(term);
|
|
760
|
+
}).length;
|
|
761
|
+
if (docsWithTerm === 0) return 0;
|
|
762
|
+
return Math.log(totalDocs / docsWithTerm);
|
|
763
|
+
}
|
|
764
|
+
function scoreEntry(entry, allEntries) {
|
|
765
|
+
const tokens = tokenize(entry.content);
|
|
766
|
+
if (tokens.length === 0) return 0;
|
|
767
|
+
const uniqueTerms = [...new Set(tokens)];
|
|
768
|
+
let totalScore = 0;
|
|
769
|
+
for (const term of uniqueTerms) {
|
|
770
|
+
const tf = calculateTF(term, tokens);
|
|
771
|
+
const idf = calculateIDF(term, allEntries);
|
|
772
|
+
totalScore += tf * idf;
|
|
773
|
+
}
|
|
774
|
+
return totalScore / tokens.length;
|
|
775
|
+
}
|
|
776
|
+
function prune(entries) {
|
|
777
|
+
if (entries.length === 0) {
|
|
778
|
+
return {
|
|
779
|
+
kept: [],
|
|
780
|
+
removed: [],
|
|
781
|
+
originalTokens: 0,
|
|
782
|
+
prunedTokens: 0
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
const originalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
|
|
786
|
+
const scored = entries.map((entry) => ({
|
|
787
|
+
entry,
|
|
788
|
+
score: scoreEntry(entry, entries)
|
|
789
|
+
}));
|
|
790
|
+
scored.sort((a, b) => b.score - a.score);
|
|
791
|
+
const keepCount = Math.max(1, Math.ceil(entries.length * (threshold / 100)));
|
|
792
|
+
const kept = scored.slice(0, keepCount).map((s) => s.entry);
|
|
793
|
+
const removed = scored.slice(keepCount).map((s) => s.entry);
|
|
794
|
+
const prunedTokens = kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);
|
|
795
|
+
return {
|
|
796
|
+
kept,
|
|
797
|
+
removed,
|
|
798
|
+
originalTokens,
|
|
799
|
+
prunedTokens
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
return {
|
|
803
|
+
prune,
|
|
804
|
+
scoreEntry
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
var init_sparse_pruner = __esm({
|
|
808
|
+
"src/core/sparse-pruner.ts"() {
|
|
809
|
+
"use strict";
|
|
810
|
+
init_esm_shims();
|
|
811
|
+
init_tokenizer();
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// src/adapters/generic.ts
|
|
816
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
817
|
+
function createGenericAdapter(memory, config) {
|
|
818
|
+
const pruner = createSparsePruner(config.pruning);
|
|
819
|
+
const scorer = createEngramScorer(config.decay);
|
|
820
|
+
const states = createConfidenceStates(config.states);
|
|
821
|
+
const btsp = createBTSPEmbedder();
|
|
822
|
+
async function optimize(context, options = {}) {
|
|
823
|
+
const startTime = Date.now();
|
|
824
|
+
const lines = context.split("\n").filter((line) => line.trim().length > 0);
|
|
825
|
+
const entries = lines.map((content) => ({
|
|
826
|
+
id: randomUUID2(),
|
|
827
|
+
content,
|
|
828
|
+
hash: hashContent(content),
|
|
829
|
+
timestamp: Date.now(),
|
|
830
|
+
score: btsp.detectBTSP(content) ? 1 : 0.5,
|
|
831
|
+
// BTSP gets high initial score
|
|
832
|
+
ttl: config.decay.defaultTTL * 3600,
|
|
833
|
+
// Convert hours to seconds
|
|
834
|
+
state: "ready",
|
|
835
|
+
accessCount: 0,
|
|
836
|
+
tags: [],
|
|
837
|
+
metadata: {},
|
|
838
|
+
isBTSP: btsp.detectBTSP(content)
|
|
839
|
+
}));
|
|
840
|
+
const tokensBefore = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
|
|
841
|
+
const scoredEntries = entries.map((entry) => ({
|
|
842
|
+
...entry,
|
|
843
|
+
score: scorer.calculateScore(entry)
|
|
844
|
+
}));
|
|
845
|
+
const statedEntries = scoredEntries.map((entry) => states.transition(entry));
|
|
846
|
+
const pruneResult = pruner.prune(statedEntries);
|
|
847
|
+
const optimizedEntries = pruneResult.kept.filter(
|
|
848
|
+
(e) => e.state === "active" || e.state === "ready"
|
|
849
|
+
);
|
|
850
|
+
const tokensAfter = optimizedEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
|
|
851
|
+
const optimizedContext = optimizedEntries.map((e) => e.content).join("\n");
|
|
852
|
+
if (!options.dryRun) {
|
|
853
|
+
for (const entry of optimizedEntries) {
|
|
854
|
+
await memory.put(entry);
|
|
855
|
+
}
|
|
856
|
+
await memory.recordOptimization({
|
|
857
|
+
timestamp: Date.now(),
|
|
858
|
+
tokens_before: tokensBefore,
|
|
859
|
+
tokens_after: tokensAfter,
|
|
860
|
+
entries_pruned: entries.length - optimizedEntries.length,
|
|
861
|
+
duration_ms: Date.now() - startTime
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
const distribution = states.getDistribution(optimizedEntries);
|
|
865
|
+
const result = {
|
|
866
|
+
optimizedContext,
|
|
867
|
+
tokensBefore,
|
|
868
|
+
tokensAfter,
|
|
869
|
+
reduction: tokensBefore > 0 ? (tokensBefore - tokensAfter) / tokensBefore : 0,
|
|
870
|
+
entriesProcessed: entries.length,
|
|
871
|
+
entriesKept: optimizedEntries.length,
|
|
872
|
+
stateDistribution: distribution,
|
|
873
|
+
durationMs: Date.now() - startTime
|
|
874
|
+
};
|
|
875
|
+
if (options.verbose) {
|
|
876
|
+
result.details = optimizedEntries.map((e) => ({
|
|
877
|
+
id: e.id,
|
|
878
|
+
score: e.score,
|
|
879
|
+
state: e.state,
|
|
880
|
+
isBTSP: e.isBTSP,
|
|
881
|
+
tokens: estimateTokens(e.content)
|
|
882
|
+
}));
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
}
|
|
886
|
+
return {
|
|
887
|
+
optimize
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
var init_generic = __esm({
|
|
891
|
+
"src/adapters/generic.ts"() {
|
|
892
|
+
"use strict";
|
|
893
|
+
init_esm_shims();
|
|
894
|
+
init_btsp_embedder();
|
|
895
|
+
init_confidence_states();
|
|
896
|
+
init_engram_scorer();
|
|
897
|
+
init_sparse_pruner();
|
|
898
|
+
init_hash();
|
|
899
|
+
init_tokenizer();
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// src/cli/commands/optimize.ts
|
|
904
|
+
var optimize_exports = {};
|
|
905
|
+
__export(optimize_exports, {
|
|
906
|
+
optimizeCommand: () => optimizeCommand
|
|
907
|
+
});
|
|
908
|
+
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
909
|
+
async function optimizeCommand(options) {
|
|
910
|
+
const { memory, dryRun = false, verbose = false } = options;
|
|
911
|
+
let input;
|
|
912
|
+
if (options.inputFile) {
|
|
913
|
+
input = await readFile(options.inputFile, "utf-8");
|
|
914
|
+
} else if (options.input) {
|
|
915
|
+
input = options.input;
|
|
916
|
+
} else {
|
|
917
|
+
throw new Error("No input provided. Use --input or --input-file");
|
|
918
|
+
}
|
|
919
|
+
const adapter = createGenericAdapter(memory, DEFAULT_CONFIG);
|
|
920
|
+
const result = await adapter.optimize(input, { dryRun, verbose });
|
|
921
|
+
if (options.outputFile) {
|
|
922
|
+
await writeFile2(options.outputFile, result.optimizedContext, "utf-8");
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
...result,
|
|
926
|
+
output: result.optimizedContext,
|
|
927
|
+
outputFile: options.outputFile
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
var init_optimize = __esm({
|
|
931
|
+
"src/cli/commands/optimize.ts"() {
|
|
932
|
+
"use strict";
|
|
933
|
+
init_esm_shims();
|
|
934
|
+
init_generic();
|
|
935
|
+
init_config();
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
// src/cli/commands/stats.ts
|
|
940
|
+
var stats_exports = {};
|
|
941
|
+
__export(stats_exports, {
|
|
942
|
+
statsCommand: () => statsCommand
|
|
943
|
+
});
|
|
944
|
+
async function statsCommand(options) {
|
|
945
|
+
const { memory, graph, reset, confirmReset, json } = options;
|
|
946
|
+
if (reset) {
|
|
947
|
+
if (confirmReset) {
|
|
948
|
+
await memory.clearOptimizationStats();
|
|
949
|
+
return {
|
|
950
|
+
totalCommands: 0,
|
|
951
|
+
totalTokensSaved: 0,
|
|
952
|
+
averageReduction: 0,
|
|
953
|
+
resetConfirmed: true
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
const stats = await memory.getOptimizationStats();
|
|
958
|
+
const totalCommands = stats.length;
|
|
959
|
+
const totalTokensSaved = stats.reduce((sum, s) => sum + (s.tokens_before - s.tokens_after), 0);
|
|
960
|
+
const averageReduction = totalCommands > 0 ? stats.reduce((sum, s) => {
|
|
961
|
+
const reduction = s.tokens_before > 0 ? (s.tokens_before - s.tokens_after) / s.tokens_before : 0;
|
|
962
|
+
return sum + reduction;
|
|
963
|
+
}, 0) / totalCommands : 0;
|
|
964
|
+
const result = {
|
|
965
|
+
totalCommands,
|
|
966
|
+
totalTokensSaved,
|
|
967
|
+
averageReduction
|
|
968
|
+
};
|
|
969
|
+
if (graph && totalCommands > 0) {
|
|
970
|
+
result.graph = generateBarChart(stats);
|
|
971
|
+
}
|
|
972
|
+
if (json) {
|
|
973
|
+
result.json = JSON.stringify(
|
|
974
|
+
{
|
|
975
|
+
totalCommands,
|
|
976
|
+
totalTokensSaved,
|
|
977
|
+
averageReduction: Math.round(averageReduction * 1e3) / 10,
|
|
978
|
+
// Convert to percentage
|
|
979
|
+
optimizations: stats.map((s) => ({
|
|
980
|
+
timestamp: s.timestamp,
|
|
981
|
+
tokensBefore: s.tokens_before,
|
|
982
|
+
tokensAfter: s.tokens_after,
|
|
983
|
+
entriesPruned: s.entries_pruned,
|
|
984
|
+
durationMs: s.duration_ms,
|
|
985
|
+
reduction: Math.round((s.tokens_before - s.tokens_after) / s.tokens_before * 1e3) / 10
|
|
986
|
+
}))
|
|
987
|
+
},
|
|
988
|
+
null,
|
|
989
|
+
2
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
994
|
+
function generateBarChart(stats) {
|
|
995
|
+
const maxBars = 20;
|
|
996
|
+
const recentStats = stats.slice(0, maxBars);
|
|
997
|
+
const lines = [];
|
|
998
|
+
lines.push("\nOptimization History (most recent first):\n");
|
|
999
|
+
const maxReduction = Math.max(...recentStats.map((s) => s.tokens_before - s.tokens_after));
|
|
1000
|
+
for (let i = 0; i < recentStats.length; i++) {
|
|
1001
|
+
const s = recentStats[i];
|
|
1002
|
+
if (!s) continue;
|
|
1003
|
+
const reduction = s.tokens_before - s.tokens_after;
|
|
1004
|
+
const reductionPct = s.tokens_before > 0 ? reduction / s.tokens_before * 100 : 0;
|
|
1005
|
+
const barLength = Math.round(reduction / maxReduction * 40);
|
|
1006
|
+
const bar = "\u2588".repeat(barLength);
|
|
1007
|
+
const date = new Date(s.timestamp);
|
|
1008
|
+
const timeStr = date.toLocaleTimeString();
|
|
1009
|
+
lines.push(`${timeStr} \u2502 ${bar} ${reductionPct.toFixed(1)}%`);
|
|
1010
|
+
}
|
|
1011
|
+
return lines.join("\n");
|
|
1012
|
+
}
|
|
1013
|
+
var init_stats = __esm({
|
|
1014
|
+
"src/cli/commands/stats.ts"() {
|
|
1015
|
+
"use strict";
|
|
1016
|
+
init_esm_shims();
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
// src/cli/commands/relay.ts
|
|
1021
|
+
var relay_exports = {};
|
|
1022
|
+
__export(relay_exports, {
|
|
1023
|
+
relayCommand: () => relayCommand
|
|
1024
|
+
});
|
|
1025
|
+
import { spawn } from "child_process";
|
|
1026
|
+
async function relayCommand(options) {
|
|
1027
|
+
const { command, args, memory, silent = false } = options;
|
|
1028
|
+
const { stdout, stderr, exitCode } = await executeCommand(command, args);
|
|
1029
|
+
const originalOutput = stdout + stderr;
|
|
1030
|
+
const adapter = createGenericAdapter(memory, DEFAULT_CONFIG);
|
|
1031
|
+
const optimizationResult = await adapter.optimize(originalOutput, {
|
|
1032
|
+
dryRun: true,
|
|
1033
|
+
// Don't save relay outputs to memory
|
|
1034
|
+
verbose: false
|
|
1035
|
+
});
|
|
1036
|
+
const result = {
|
|
1037
|
+
exitCode,
|
|
1038
|
+
originalOutput,
|
|
1039
|
+
optimizedOutput: optimizationResult.optimizedContext,
|
|
1040
|
+
tokensBefore: optimizationResult.tokensBefore,
|
|
1041
|
+
tokensAfter: optimizationResult.tokensAfter,
|
|
1042
|
+
reduction: optimizationResult.reduction
|
|
1043
|
+
};
|
|
1044
|
+
if (!silent && result.tokensBefore > 0) {
|
|
1045
|
+
const reductionPct = (result.reduction * 100).toFixed(1);
|
|
1046
|
+
result.summary = `\u{1F4CA} ${result.tokensBefore} \u2192 ${result.tokensAfter} tokens (${reductionPct}% reduction)`;
|
|
1047
|
+
}
|
|
1048
|
+
return result;
|
|
1049
|
+
}
|
|
1050
|
+
function executeCommand(command, args) {
|
|
1051
|
+
return new Promise((resolve2) => {
|
|
1052
|
+
const child = spawn(command, args, {
|
|
1053
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
1054
|
+
});
|
|
1055
|
+
let stdout = "";
|
|
1056
|
+
let stderr = "";
|
|
1057
|
+
child.stdout?.on("data", (data) => {
|
|
1058
|
+
stdout += data.toString();
|
|
1059
|
+
});
|
|
1060
|
+
child.stderr?.on("data", (data) => {
|
|
1061
|
+
stderr += data.toString();
|
|
1062
|
+
});
|
|
1063
|
+
child.on("close", (code) => {
|
|
1064
|
+
resolve2({
|
|
1065
|
+
stdout,
|
|
1066
|
+
stderr,
|
|
1067
|
+
exitCode: code ?? 0
|
|
1068
|
+
});
|
|
1069
|
+
});
|
|
1070
|
+
child.on("error", (error) => {
|
|
1071
|
+
resolve2({
|
|
1072
|
+
stdout,
|
|
1073
|
+
stderr: error.message,
|
|
1074
|
+
exitCode: 1
|
|
1075
|
+
});
|
|
1076
|
+
});
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
var init_relay = __esm({
|
|
1080
|
+
"src/cli/commands/relay.ts"() {
|
|
1081
|
+
"use strict";
|
|
1082
|
+
init_esm_shims();
|
|
1083
|
+
init_generic();
|
|
1084
|
+
init_config();
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
// src/core/sleep-compressor.ts
|
|
1089
|
+
function createSleepCompressor() {
|
|
1090
|
+
const scorer = createEngramScorer({ defaultTTL: 24, decayThreshold: 0.95 });
|
|
1091
|
+
function consolidate(entries) {
|
|
1092
|
+
const startTime = Date.now();
|
|
1093
|
+
const originalCount = entries.length;
|
|
1094
|
+
const now = Date.now();
|
|
1095
|
+
const nonDecayed = entries.filter((entry) => {
|
|
1096
|
+
const ageInSeconds = (now - entry.timestamp) / 1e3;
|
|
1097
|
+
const decay = scorer.calculateDecay(ageInSeconds, entry.ttl);
|
|
1098
|
+
return decay < 0.95;
|
|
1099
|
+
});
|
|
1100
|
+
const decayedRemoved = originalCount - nonDecayed.length;
|
|
1101
|
+
const duplicateGroups = findDuplicates(nonDecayed);
|
|
1102
|
+
const merged = mergeDuplicates(duplicateGroups);
|
|
1103
|
+
const duplicateIds = new Set(duplicateGroups.flatMap((g) => g.entries.map((e) => e.id)));
|
|
1104
|
+
const nonDuplicates = nonDecayed.filter((e) => !duplicateIds.has(e.id));
|
|
1105
|
+
const kept = [...merged, ...nonDuplicates];
|
|
1106
|
+
const removed = entries.filter((e) => !kept.some((k) => k.id === e.id));
|
|
1107
|
+
const duplicatesRemoved = duplicateGroups.reduce((sum, g) => sum + (g.entries.length - 1), 0);
|
|
1108
|
+
return {
|
|
1109
|
+
kept,
|
|
1110
|
+
removed,
|
|
1111
|
+
entriesBefore: originalCount,
|
|
1112
|
+
entriesAfter: kept.length,
|
|
1113
|
+
decayedRemoved,
|
|
1114
|
+
duplicatesRemoved,
|
|
1115
|
+
compressionRatio: originalCount > 0 ? kept.length / originalCount : 0,
|
|
1116
|
+
durationMs: Date.now() - startTime
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
function findDuplicates(entries) {
|
|
1120
|
+
const groups = [];
|
|
1121
|
+
const processed = /* @__PURE__ */ new Set();
|
|
1122
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1123
|
+
const entry = entries[i];
|
|
1124
|
+
if (!entry || processed.has(entry.id)) continue;
|
|
1125
|
+
const duplicates = entries.filter((e, idx) => idx !== i && e.hash === entry.hash);
|
|
1126
|
+
if (duplicates.length > 0) {
|
|
1127
|
+
const group = {
|
|
1128
|
+
entries: [entry, ...duplicates],
|
|
1129
|
+
similarity: 1
|
|
1130
|
+
// Exact match
|
|
1131
|
+
};
|
|
1132
|
+
groups.push(group);
|
|
1133
|
+
processed.add(entry.id);
|
|
1134
|
+
for (const dup of duplicates) {
|
|
1135
|
+
processed.add(dup.id);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1140
|
+
const entryI = entries[i];
|
|
1141
|
+
if (!entryI || processed.has(entryI.id)) continue;
|
|
1142
|
+
for (let j = i + 1; j < entries.length; j++) {
|
|
1143
|
+
const entryJ = entries[j];
|
|
1144
|
+
if (!entryJ || processed.has(entryJ.id)) continue;
|
|
1145
|
+
const similarity = cosineSimilarity(entryI.content, entryJ.content);
|
|
1146
|
+
if (similarity >= 0.85) {
|
|
1147
|
+
const group = {
|
|
1148
|
+
entries: [entryI, entryJ],
|
|
1149
|
+
similarity
|
|
1150
|
+
};
|
|
1151
|
+
groups.push(group);
|
|
1152
|
+
processed.add(entryI.id);
|
|
1153
|
+
processed.add(entryJ.id);
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return groups;
|
|
1159
|
+
}
|
|
1160
|
+
function mergeDuplicates(groups) {
|
|
1161
|
+
const merged = [];
|
|
1162
|
+
for (const group of groups) {
|
|
1163
|
+
const sorted = [...group.entries].sort((a, b) => b.score - a.score);
|
|
1164
|
+
const best = sorted[0];
|
|
1165
|
+
if (!best) continue;
|
|
1166
|
+
const totalAccessCount = group.entries.reduce((sum, e) => sum + e.accessCount, 0);
|
|
1167
|
+
const allTags = new Set(group.entries.flatMap((e) => e.tags));
|
|
1168
|
+
merged.push({
|
|
1169
|
+
...best,
|
|
1170
|
+
accessCount: totalAccessCount,
|
|
1171
|
+
tags: Array.from(allTags)
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
return merged;
|
|
1175
|
+
}
|
|
1176
|
+
function cosineSimilarity(text1, text2) {
|
|
1177
|
+
const words1 = tokenize(text1);
|
|
1178
|
+
const words2 = tokenize(text2);
|
|
1179
|
+
const vocab = /* @__PURE__ */ new Set([...words1, ...words2]);
|
|
1180
|
+
const vec1 = {};
|
|
1181
|
+
const vec2 = {};
|
|
1182
|
+
for (const word of vocab) {
|
|
1183
|
+
vec1[word] = words1.filter((w) => w === word).length;
|
|
1184
|
+
vec2[word] = words2.filter((w) => w === word).length;
|
|
1185
|
+
}
|
|
1186
|
+
let dotProduct = 0;
|
|
1187
|
+
let mag1 = 0;
|
|
1188
|
+
let mag2 = 0;
|
|
1189
|
+
for (const word of vocab) {
|
|
1190
|
+
const count1 = vec1[word] ?? 0;
|
|
1191
|
+
const count2 = vec2[word] ?? 0;
|
|
1192
|
+
dotProduct += count1 * count2;
|
|
1193
|
+
mag1 += count1 * count1;
|
|
1194
|
+
mag2 += count2 * count2;
|
|
1195
|
+
}
|
|
1196
|
+
mag1 = Math.sqrt(mag1);
|
|
1197
|
+
mag2 = Math.sqrt(mag2);
|
|
1198
|
+
if (mag1 === 0 || mag2 === 0) return 0;
|
|
1199
|
+
return dotProduct / (mag1 * mag2);
|
|
1200
|
+
}
|
|
1201
|
+
function tokenize(text) {
|
|
1202
|
+
return text.toLowerCase().split(/\s+/).filter((word) => word.length > 0);
|
|
1203
|
+
}
|
|
1204
|
+
return {
|
|
1205
|
+
consolidate,
|
|
1206
|
+
findDuplicates,
|
|
1207
|
+
mergeDuplicates
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
var init_sleep_compressor = __esm({
|
|
1211
|
+
"src/core/sleep-compressor.ts"() {
|
|
1212
|
+
"use strict";
|
|
1213
|
+
init_esm_shims();
|
|
1214
|
+
init_engram_scorer();
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
// src/cli/commands/consolidate.ts
|
|
1219
|
+
var consolidate_exports = {};
|
|
1220
|
+
__export(consolidate_exports, {
|
|
1221
|
+
consolidateCommand: () => consolidateCommand
|
|
1222
|
+
});
|
|
1223
|
+
async function consolidateCommand(options) {
|
|
1224
|
+
const { memory } = options;
|
|
1225
|
+
const allIds = await memory.list();
|
|
1226
|
+
const allEntries = await Promise.all(
|
|
1227
|
+
allIds.map(async (id) => {
|
|
1228
|
+
const entry = await memory.get(id);
|
|
1229
|
+
return entry;
|
|
1230
|
+
})
|
|
1231
|
+
);
|
|
1232
|
+
const entries = allEntries.filter((e) => e !== null);
|
|
1233
|
+
const compressor = createSleepCompressor();
|
|
1234
|
+
const result = compressor.consolidate(entries);
|
|
1235
|
+
for (const removed of result.removed) {
|
|
1236
|
+
await memory.delete(removed.id);
|
|
1237
|
+
}
|
|
1238
|
+
for (const kept of result.kept) {
|
|
1239
|
+
await memory.put(kept);
|
|
1240
|
+
}
|
|
1241
|
+
await memory.compact();
|
|
1242
|
+
return {
|
|
1243
|
+
entriesBefore: result.entriesBefore,
|
|
1244
|
+
entriesAfter: result.entriesAfter,
|
|
1245
|
+
decayedRemoved: result.decayedRemoved,
|
|
1246
|
+
duplicatesRemoved: result.duplicatesRemoved,
|
|
1247
|
+
compressionRatio: result.compressionRatio,
|
|
1248
|
+
durationMs: result.durationMs,
|
|
1249
|
+
vacuumCompleted: true
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
var init_consolidate = __esm({
|
|
1253
|
+
"src/cli/commands/consolidate.ts"() {
|
|
1254
|
+
"use strict";
|
|
1255
|
+
init_esm_shims();
|
|
1256
|
+
init_sleep_compressor();
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
// src/cli/commands/config.ts
|
|
1261
|
+
var config_exports = {};
|
|
1262
|
+
__export(config_exports, {
|
|
1263
|
+
configCommand: () => configCommand
|
|
1264
|
+
});
|
|
1265
|
+
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1266
|
+
import { load as parseYAML, dump as stringifyYAML } from "js-yaml";
|
|
1267
|
+
async function configCommand(options) {
|
|
1268
|
+
const { configPath, subcommand, key, value, json } = options;
|
|
1269
|
+
try {
|
|
1270
|
+
const configYAML = readFileSync2(configPath, "utf-8");
|
|
1271
|
+
const config = parseYAML(configYAML);
|
|
1272
|
+
if (!subcommand) {
|
|
1273
|
+
if (json) {
|
|
1274
|
+
return {
|
|
1275
|
+
success: true,
|
|
1276
|
+
json: JSON.stringify(config, null, 2)
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
return {
|
|
1280
|
+
success: true,
|
|
1281
|
+
editorPath: configPath,
|
|
1282
|
+
message: `Config file: ${configPath}`
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
if (subcommand === "get") {
|
|
1286
|
+
if (!key) {
|
|
1287
|
+
return {
|
|
1288
|
+
success: false,
|
|
1289
|
+
error: "Key required for get command"
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
const schema = CONFIG_SCHEMA[key];
|
|
1293
|
+
if (!schema) {
|
|
1294
|
+
return {
|
|
1295
|
+
success: false,
|
|
1296
|
+
error: `Invalid key: ${key}. Run 'sparn config' to see available keys.`
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
const retrievedValue = getNestedValue(
|
|
1300
|
+
config,
|
|
1301
|
+
schema.path
|
|
1302
|
+
);
|
|
1303
|
+
if (json) {
|
|
1304
|
+
return {
|
|
1305
|
+
success: true,
|
|
1306
|
+
value: retrievedValue,
|
|
1307
|
+
json: JSON.stringify({ key, value: retrievedValue }, null, 2)
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
success: true,
|
|
1312
|
+
value: retrievedValue,
|
|
1313
|
+
message: String(retrievedValue)
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
if (subcommand === "set") {
|
|
1317
|
+
if (!key) {
|
|
1318
|
+
return {
|
|
1319
|
+
success: false,
|
|
1320
|
+
error: "Key required for set command"
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
if (value === void 0) {
|
|
1324
|
+
return {
|
|
1325
|
+
success: false,
|
|
1326
|
+
error: "Value required for set command"
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
const schema = CONFIG_SCHEMA[key];
|
|
1330
|
+
if (!schema) {
|
|
1331
|
+
return {
|
|
1332
|
+
success: false,
|
|
1333
|
+
error: `Invalid key: ${key}. Run 'sparn config' to see available keys.`
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
const parsedValue = schema.parse(value);
|
|
1337
|
+
if (!schema.validate(parsedValue)) {
|
|
1338
|
+
return {
|
|
1339
|
+
success: false,
|
|
1340
|
+
error: `Invalid value for ${key}: ${schema.errorMessage}`
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
setNestedValue(config, schema.path, parsedValue);
|
|
1344
|
+
const updatedYAML = stringifyYAML(config);
|
|
1345
|
+
writeFileSync(configPath, updatedYAML, "utf-8");
|
|
1346
|
+
return {
|
|
1347
|
+
success: true,
|
|
1348
|
+
message: `Config updated: ${key} = ${parsedValue}`
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
return {
|
|
1352
|
+
success: false,
|
|
1353
|
+
error: `Unknown subcommand: ${subcommand}`
|
|
1354
|
+
};
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
return {
|
|
1357
|
+
success: false,
|
|
1358
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
function getNestedValue(obj, path2) {
|
|
1363
|
+
let current = obj;
|
|
1364
|
+
for (const key of path2) {
|
|
1365
|
+
if (current && typeof current === "object" && !Array.isArray(current) && key in current) {
|
|
1366
|
+
current = current[key];
|
|
1367
|
+
} else {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
return current;
|
|
1372
|
+
}
|
|
1373
|
+
function setNestedValue(obj, path2, value) {
|
|
1374
|
+
let current = obj;
|
|
1375
|
+
for (let i = 0; i < path2.length - 1; i++) {
|
|
1376
|
+
const key = path2[i];
|
|
1377
|
+
if (!key) continue;
|
|
1378
|
+
if (!current[key] || typeof current[key] !== "object") {
|
|
1379
|
+
current[key] = {};
|
|
1380
|
+
}
|
|
1381
|
+
current = current[key];
|
|
1382
|
+
}
|
|
1383
|
+
const lastKey = path2[path2.length - 1];
|
|
1384
|
+
if (lastKey) {
|
|
1385
|
+
current[lastKey] = value;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
var CONFIG_SCHEMA;
|
|
1389
|
+
var init_config2 = __esm({
|
|
1390
|
+
"src/cli/commands/config.ts"() {
|
|
1391
|
+
"use strict";
|
|
1392
|
+
init_esm_shims();
|
|
1393
|
+
CONFIG_SCHEMA = {
|
|
1394
|
+
"pruning.threshold": {
|
|
1395
|
+
path: ["pruning", "threshold"],
|
|
1396
|
+
validate: (v) => typeof v === "number" && v >= 1 && v <= 100,
|
|
1397
|
+
errorMessage: "threshold must be between 1-100",
|
|
1398
|
+
parse: (v) => Number.parseInt(v, 10)
|
|
1399
|
+
},
|
|
1400
|
+
"pruning.aggressiveness": {
|
|
1401
|
+
path: ["pruning", "aggressiveness"],
|
|
1402
|
+
validate: (v) => typeof v === "number" && v >= 0 && v <= 100,
|
|
1403
|
+
errorMessage: "aggressiveness must be between 0-100",
|
|
1404
|
+
parse: (v) => Number.parseInt(v, 10)
|
|
1405
|
+
},
|
|
1406
|
+
"decay.defaultTTL": {
|
|
1407
|
+
path: ["decay", "defaultTTL"],
|
|
1408
|
+
validate: (v) => typeof v === "number" && v > 0,
|
|
1409
|
+
errorMessage: "defaultTTL must be a positive number (hours)",
|
|
1410
|
+
parse: (v) => Number.parseFloat(v)
|
|
1411
|
+
},
|
|
1412
|
+
"decay.decayThreshold": {
|
|
1413
|
+
path: ["decay", "decayThreshold"],
|
|
1414
|
+
validate: (v) => typeof v === "number" && v >= 0 && v <= 1,
|
|
1415
|
+
errorMessage: "decayThreshold must be between 0.0-1.0",
|
|
1416
|
+
parse: (v) => Number.parseFloat(v)
|
|
1417
|
+
},
|
|
1418
|
+
"states.activeThreshold": {
|
|
1419
|
+
path: ["states", "activeThreshold"],
|
|
1420
|
+
validate: (v) => typeof v === "number" && v >= 0 && v <= 1,
|
|
1421
|
+
errorMessage: "activeThreshold must be between 0.0-1.0",
|
|
1422
|
+
parse: (v) => Number.parseFloat(v)
|
|
1423
|
+
},
|
|
1424
|
+
"states.readyThreshold": {
|
|
1425
|
+
path: ["states", "readyThreshold"],
|
|
1426
|
+
validate: (v) => typeof v === "number" && v >= 0 && v <= 1,
|
|
1427
|
+
errorMessage: "readyThreshold must be between 0.0-1.0",
|
|
1428
|
+
parse: (v) => Number.parseFloat(v)
|
|
1429
|
+
},
|
|
1430
|
+
agent: {
|
|
1431
|
+
path: ["agent"],
|
|
1432
|
+
validate: (v) => v === "claude-code" || v === "generic",
|
|
1433
|
+
errorMessage: 'agent must be "claude-code" or "generic"',
|
|
1434
|
+
parse: (v) => v
|
|
1435
|
+
},
|
|
1436
|
+
"ui.colors": {
|
|
1437
|
+
path: ["ui", "colors"],
|
|
1438
|
+
validate: (v) => typeof v === "boolean",
|
|
1439
|
+
errorMessage: "colors must be true or false",
|
|
1440
|
+
parse: (v) => v === "true"
|
|
1441
|
+
},
|
|
1442
|
+
"ui.sounds": {
|
|
1443
|
+
path: ["ui", "sounds"],
|
|
1444
|
+
validate: (v) => typeof v === "boolean",
|
|
1445
|
+
errorMessage: "sounds must be true or false",
|
|
1446
|
+
parse: (v) => v === "true"
|
|
1447
|
+
},
|
|
1448
|
+
"ui.verbose": {
|
|
1449
|
+
path: ["ui", "verbose"],
|
|
1450
|
+
validate: (v) => typeof v === "boolean",
|
|
1451
|
+
errorMessage: "verbose must be true or false",
|
|
1452
|
+
parse: (v) => v === "true"
|
|
1453
|
+
},
|
|
1454
|
+
autoConsolidate: {
|
|
1455
|
+
path: ["autoConsolidate"],
|
|
1456
|
+
validate: (v) => v === null || typeof v === "number" && v > 0,
|
|
1457
|
+
errorMessage: "autoConsolidate must be a positive number (hours) or null",
|
|
1458
|
+
parse: (v) => v === "null" ? null : Number.parseFloat(v)
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
// src/cli/index.ts
|
|
1465
|
+
init_esm_shims();
|
|
1466
|
+
init_banner();
|
|
1467
|
+
import { spawn as spawn2 } from "child_process";
|
|
1468
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1469
|
+
import { dirname as dirname2, join as join2, resolve } from "path";
|
|
1470
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1471
|
+
import { Command } from "commander";
|
|
1472
|
+
function getVersion2() {
|
|
1473
|
+
try {
|
|
1474
|
+
const pkg = JSON.parse(readFileSync3(join2(process.cwd(), "package.json"), "utf-8"));
|
|
1475
|
+
return pkg.version;
|
|
1476
|
+
} catch {
|
|
1477
|
+
const __filename2 = fileURLToPath3(import.meta.url);
|
|
1478
|
+
const __dirname2 = dirname2(__filename2);
|
|
1479
|
+
const pkg = JSON.parse(readFileSync3(join2(__dirname2, "../../package.json"), "utf-8"));
|
|
1480
|
+
return pkg.version;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
var VERSION2 = getVersion2();
|
|
1484
|
+
async function handleError(error, context) {
|
|
1485
|
+
const { errorRed: errorRed2, synapseViolet: synapseViolet2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1486
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1487
|
+
const stack = error instanceof Error ? error.stack : void 0;
|
|
1488
|
+
console.error(errorRed2("\n\u2717 Error:"), errorMsg);
|
|
1489
|
+
if (context) {
|
|
1490
|
+
console.error(errorRed2("Context:"), context);
|
|
1491
|
+
}
|
|
1492
|
+
if (errorMsg.includes("SQLITE") || errorMsg.includes("database")) {
|
|
1493
|
+
console.error(synapseViolet2("\n\u{1F4A1} Database issue detected:"));
|
|
1494
|
+
console.error(" Try running: rm -rf .sparn/ && sparn init");
|
|
1495
|
+
console.error(" This will reinitialize your Sparn database.\n");
|
|
1496
|
+
}
|
|
1497
|
+
if (errorMsg.includes("EACCES") || errorMsg.includes("permission")) {
|
|
1498
|
+
console.error(synapseViolet2("\n\u{1F4A1} Permission issue detected:"));
|
|
1499
|
+
console.error(" Check file permissions in .sparn/ directory");
|
|
1500
|
+
console.error(" Try: chmod -R u+rw .sparn/\n");
|
|
1501
|
+
}
|
|
1502
|
+
if (errorMsg.includes("ENOENT") || errorMsg.includes("no such file")) {
|
|
1503
|
+
console.error(synapseViolet2("\n\u{1F4A1} File not found:"));
|
|
1504
|
+
console.error(" Make sure you have run: sparn init");
|
|
1505
|
+
console.error(" Or check that the specified file exists.\n");
|
|
1506
|
+
}
|
|
1507
|
+
if (errorMsg.includes("out of memory") || errorMsg.includes("heap")) {
|
|
1508
|
+
console.error(synapseViolet2("\n\u{1F4A1} Memory issue detected:"));
|
|
1509
|
+
console.error(" Try processing smaller chunks of context");
|
|
1510
|
+
console.error(" Or increase Node.js memory: NODE_OPTIONS=--max-old-space-size=4096\n");
|
|
1511
|
+
}
|
|
1512
|
+
if (process.env["SPARN_DEBUG"] === "true" && stack) {
|
|
1513
|
+
console.error(errorRed2("\nStack trace:"));
|
|
1514
|
+
console.error(stack);
|
|
1515
|
+
} else {
|
|
1516
|
+
console.error(" Run with SPARN_DEBUG=true for stack trace\n");
|
|
1517
|
+
}
|
|
1518
|
+
process.exit(1);
|
|
1519
|
+
}
|
|
1520
|
+
process.on("unhandledRejection", (reason) => {
|
|
1521
|
+
void handleError(reason, "Unhandled promise rejection");
|
|
1522
|
+
});
|
|
1523
|
+
process.on("uncaughtException", (error) => {
|
|
1524
|
+
void handleError(error, "Uncaught exception");
|
|
1525
|
+
});
|
|
1526
|
+
var program = new Command();
|
|
1527
|
+
program.name("sparn").description("Neuroscience-inspired context optimization for AI coding agents").version(VERSION2, "-v, --version", "Output the current version").helpOption("-h, --help", "Display help for command");
|
|
1528
|
+
program.command("init").description("Initialize Sparn in the current project").option("-f, --force", "Force overwrite if .sparn/ already exists").addHelpText(
|
|
1529
|
+
"after",
|
|
1530
|
+
`
|
|
1531
|
+
Examples:
|
|
1532
|
+
$ sparn init # Initialize in current directory
|
|
1533
|
+
$ sparn init --force # Overwrite existing .sparn/ directory
|
|
1534
|
+
|
|
1535
|
+
Files Created:
|
|
1536
|
+
.sparn/config.yaml # Configuration with neuroscience parameters
|
|
1537
|
+
.sparn/memory.db # SQLite database for context storage
|
|
1538
|
+
|
|
1539
|
+
Next Steps:
|
|
1540
|
+
After initialization, use 'sparn optimize' to start optimizing context.
|
|
1541
|
+
`
|
|
1542
|
+
).action(async (options) => {
|
|
1543
|
+
const { initCommand: initCommand2, displayInitSuccess: displayInitSuccess2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
1544
|
+
const { createInitSpinner: createInitSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
1545
|
+
const { neuralCyan: neuralCyan2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1546
|
+
const spinner = createInitSpinner2("\u{1F9E0} Initializing Sparn...");
|
|
1547
|
+
try {
|
|
1548
|
+
spinner.start();
|
|
1549
|
+
spinner.text = "\u{1F4C1} Creating .sparn/ directory...";
|
|
1550
|
+
const result = await initCommand2({ force: options.force });
|
|
1551
|
+
spinner.succeed(neuralCyan2("Sparn initialized successfully!"));
|
|
1552
|
+
displayInitSuccess2(result);
|
|
1553
|
+
} catch (error) {
|
|
1554
|
+
spinner.fail(errorRed2("Initialization failed"));
|
|
1555
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
1556
|
+
process.exit(1);
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
program.command("optimize").description("Optimize context memory using neuroscience principles").option("-i, --input <file>", "Input file path").option("-o, --output <file>", "Output file path").option("--dry-run", "Run without saving to memory").option("--verbose", "Show detailed per-entry scores").addHelpText(
|
|
1560
|
+
"after",
|
|
1561
|
+
`
|
|
1562
|
+
Examples:
|
|
1563
|
+
$ sparn optimize -i context.txt -o optimized.txt # Optimize file
|
|
1564
|
+
$ cat context.txt | sparn optimize # Optimize from stdin
|
|
1565
|
+
$ sparn optimize -i context.txt --dry-run # Preview without saving
|
|
1566
|
+
$ sparn optimize -i context.txt --verbose # Show entry scores
|
|
1567
|
+
|
|
1568
|
+
How It Works:
|
|
1569
|
+
1. Sparse Coding: Keeps only 2-5% most relevant context
|
|
1570
|
+
2. Engram Theory: Applies decay to old memories
|
|
1571
|
+
3. Multi-State Synapses: Classifies as silent/ready/active
|
|
1572
|
+
4. BTSP: Locks critical events (errors, stack traces)
|
|
1573
|
+
5. Sleep Replay: Consolidates and compresses
|
|
1574
|
+
|
|
1575
|
+
Typical Results:
|
|
1576
|
+
\u2022 60-90% token reduction
|
|
1577
|
+
\u2022 Preserved task-critical information
|
|
1578
|
+
\u2022 Enhanced AI agent focus
|
|
1579
|
+
`
|
|
1580
|
+
).action(async (options) => {
|
|
1581
|
+
const { createKVMemory: createKVMemory2 } = await Promise.resolve().then(() => (init_kv_memory(), kv_memory_exports));
|
|
1582
|
+
const { optimizeCommand: optimizeCommand2 } = await Promise.resolve().then(() => (init_optimize(), optimize_exports));
|
|
1583
|
+
const { createOptimizeSpinner: createOptimizeSpinner2, showTokenSavings: showTokenSavings2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
1584
|
+
const { neuralCyan: neuralCyan2, synapseViolet: synapseViolet2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1585
|
+
const spinner = createOptimizeSpinner2("\u{1F9E0} Initializing optimization...");
|
|
1586
|
+
try {
|
|
1587
|
+
spinner.start();
|
|
1588
|
+
let input;
|
|
1589
|
+
if (!options.input && process.stdin.isTTY === false) {
|
|
1590
|
+
spinner.text = "\u{1F4D6} Reading context from stdin...";
|
|
1591
|
+
const chunks = [];
|
|
1592
|
+
for await (const chunk of process.stdin) {
|
|
1593
|
+
chunks.push(chunk);
|
|
1594
|
+
}
|
|
1595
|
+
input = Buffer.concat(chunks).toString("utf-8");
|
|
1596
|
+
} else if (options.input) {
|
|
1597
|
+
spinner.text = `\u{1F4D6} Reading context from ${options.input}...`;
|
|
1598
|
+
}
|
|
1599
|
+
spinner.text = "\u{1F4BE} Loading memory database...";
|
|
1600
|
+
const dbPath = resolve(process.cwd(), ".sparn/memory.db");
|
|
1601
|
+
const memory = await createKVMemory2(dbPath);
|
|
1602
|
+
spinner.text = "\u26A1 Applying neuroscience principles...";
|
|
1603
|
+
const result = await optimizeCommand2({
|
|
1604
|
+
input,
|
|
1605
|
+
inputFile: options.input,
|
|
1606
|
+
outputFile: options.output,
|
|
1607
|
+
memory,
|
|
1608
|
+
dryRun: options.dryRun || false,
|
|
1609
|
+
verbose: options.verbose || false
|
|
1610
|
+
});
|
|
1611
|
+
spinner.succeed(neuralCyan2(`Optimization complete in ${result.durationMs}ms!`));
|
|
1612
|
+
showTokenSavings2(result.tokensBefore, result.tokensAfter, result.reduction);
|
|
1613
|
+
console.log(synapseViolet2(" Entry Distribution:"));
|
|
1614
|
+
console.log(` \u2022 Processed: ${result.entriesProcessed}`);
|
|
1615
|
+
console.log(` \u2022 Kept: ${result.entriesKept}`);
|
|
1616
|
+
console.log(` \u2022 Active: ${result.stateDistribution.active}`);
|
|
1617
|
+
console.log(` \u2022 Ready: ${result.stateDistribution.ready}`);
|
|
1618
|
+
console.log(` \u2022 Silent: ${result.stateDistribution.silent}
|
|
1619
|
+
`);
|
|
1620
|
+
if (options.verbose && result.details) {
|
|
1621
|
+
console.log(neuralCyan2(" \u{1F4CB} Entry Details:"));
|
|
1622
|
+
for (const detail of result.details) {
|
|
1623
|
+
console.log(
|
|
1624
|
+
` ${detail.id.substring(0, 8)}: score=${detail.score.toFixed(2)}, state=${detail.state}, tokens=${detail.tokens}`
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
console.log();
|
|
1628
|
+
}
|
|
1629
|
+
if (!options.output) {
|
|
1630
|
+
console.log(result.output);
|
|
1631
|
+
}
|
|
1632
|
+
await memory.close();
|
|
1633
|
+
} catch (error) {
|
|
1634
|
+
spinner.fail(errorRed2("Optimization failed"));
|
|
1635
|
+
console.error(errorRed2("Error:"), error instanceof Error ? error.message : String(error));
|
|
1636
|
+
process.exit(1);
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
program.command("stats").description("View optimization statistics").option("--graph", "Display ASCII bar chart of optimization history").option("--reset", "Clear all optimization statistics").option("--json", "Output statistics in JSON format").addHelpText(
|
|
1640
|
+
"after",
|
|
1641
|
+
`
|
|
1642
|
+
Examples:
|
|
1643
|
+
$ sparn stats # View summary statistics
|
|
1644
|
+
$ sparn stats --graph # Show ASCII chart of optimization history
|
|
1645
|
+
$ sparn stats --json # Output as JSON for automation
|
|
1646
|
+
$ sparn stats --reset # Clear all statistics (with confirmation)
|
|
1647
|
+
|
|
1648
|
+
Tracked Metrics:
|
|
1649
|
+
\u2022 Total optimizations performed
|
|
1650
|
+
\u2022 Total tokens saved across all runs
|
|
1651
|
+
\u2022 Average reduction percentage
|
|
1652
|
+
\u2022 Per-run token before/after counts
|
|
1653
|
+
\u2022 Optimization duration
|
|
1654
|
+
`
|
|
1655
|
+
).action(async (options) => {
|
|
1656
|
+
const { createKVMemory: createKVMemory2 } = await Promise.resolve().then(() => (init_kv_memory(), kv_memory_exports));
|
|
1657
|
+
const { statsCommand: statsCommand2 } = await Promise.resolve().then(() => (init_stats(), stats_exports));
|
|
1658
|
+
const { createStatsSpinner: createStatsSpinner2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
1659
|
+
const { neuralCyan: neuralCyan2, synapseViolet: synapseViolet2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1660
|
+
const spinner = options.graph ? createStatsSpinner2("\u{1F4CA} Generating statistics...") : null;
|
|
1661
|
+
try {
|
|
1662
|
+
if (spinner) spinner.start();
|
|
1663
|
+
if (spinner) spinner.text = "\u{1F4BE} Loading optimization history...";
|
|
1664
|
+
const dbPath = resolve(process.cwd(), ".sparn/memory.db");
|
|
1665
|
+
const memory = await createKVMemory2(dbPath);
|
|
1666
|
+
let confirmReset = false;
|
|
1667
|
+
if (options.reset) {
|
|
1668
|
+
if (spinner) spinner.stop();
|
|
1669
|
+
console.log(synapseViolet2("Warning: This will clear all optimization statistics."));
|
|
1670
|
+
confirmReset = true;
|
|
1671
|
+
}
|
|
1672
|
+
if (spinner) spinner.text = "\u{1F4C8} Calculating statistics...";
|
|
1673
|
+
const result = await statsCommand2({
|
|
1674
|
+
memory,
|
|
1675
|
+
graph: options.graph || false,
|
|
1676
|
+
reset: options.reset || false,
|
|
1677
|
+
confirmReset,
|
|
1678
|
+
json: options.json || false
|
|
1679
|
+
});
|
|
1680
|
+
if (spinner) spinner.succeed(neuralCyan2("Statistics ready!"));
|
|
1681
|
+
if (options.json) {
|
|
1682
|
+
console.log(result.json);
|
|
1683
|
+
} else if (options.reset && result.resetConfirmed) {
|
|
1684
|
+
console.log(neuralCyan2("\n\u2713 Statistics cleared\n"));
|
|
1685
|
+
} else {
|
|
1686
|
+
console.log(neuralCyan2("\n\u{1F4CA} Optimization Statistics\n"));
|
|
1687
|
+
console.log(` Total optimizations: ${result.totalCommands}`);
|
|
1688
|
+
console.log(` Total tokens saved: ${result.totalTokensSaved.toLocaleString()}`);
|
|
1689
|
+
console.log(` Average reduction: ${(result.averageReduction * 100).toFixed(1)}%`);
|
|
1690
|
+
if (options.graph && result.graph) {
|
|
1691
|
+
console.log(result.graph);
|
|
1692
|
+
}
|
|
1693
|
+
console.log();
|
|
1694
|
+
}
|
|
1695
|
+
await memory.close();
|
|
1696
|
+
} catch (error) {
|
|
1697
|
+
if (spinner) spinner.fail(errorRed2("Statistics failed"));
|
|
1698
|
+
console.error(errorRed2("Error:"), error instanceof Error ? error.message : String(error));
|
|
1699
|
+
process.exit(1);
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
program.command("relay <command> [args...]").description("Proxy a CLI command through optimization").option("--silent", "Suppress token savings summary").addHelpText(
|
|
1703
|
+
"after",
|
|
1704
|
+
`
|
|
1705
|
+
Examples:
|
|
1706
|
+
$ sparn relay git log # Run 'git log' and optimize output
|
|
1707
|
+
$ sparn relay npm test # Run 'npm test' and optimize output
|
|
1708
|
+
$ sparn relay gh pr view 123 # Optimize GitHub CLI output
|
|
1709
|
+
$ sparn relay ls -la --silent # Suppress optimization summary
|
|
1710
|
+
|
|
1711
|
+
Use Cases:
|
|
1712
|
+
\u2022 Wrap verbose CLI commands (git log, gh pr view)
|
|
1713
|
+
\u2022 Optimize test runner output
|
|
1714
|
+
\u2022 Compress build logs
|
|
1715
|
+
\u2022 Filter CI/CD output for AI agent consumption
|
|
1716
|
+
|
|
1717
|
+
The relay command passes the exit code from the wrapped command.
|
|
1718
|
+
`
|
|
1719
|
+
).action(async (command, args, options) => {
|
|
1720
|
+
const { createKVMemory: createKVMemory2 } = await Promise.resolve().then(() => (init_kv_memory(), kv_memory_exports));
|
|
1721
|
+
const { relayCommand: relayCommand2 } = await Promise.resolve().then(() => (init_relay(), relay_exports));
|
|
1722
|
+
const { neuralCyan: neuralCyan2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1723
|
+
try {
|
|
1724
|
+
const dbPath = resolve(process.cwd(), ".sparn/memory.db");
|
|
1725
|
+
const memory = await createKVMemory2(dbPath);
|
|
1726
|
+
const result = await relayCommand2({
|
|
1727
|
+
command,
|
|
1728
|
+
args: args || [],
|
|
1729
|
+
memory,
|
|
1730
|
+
silent: options.silent || false
|
|
1731
|
+
});
|
|
1732
|
+
console.log(result.optimizedOutput);
|
|
1733
|
+
if (result.summary) {
|
|
1734
|
+
console.error(neuralCyan2(`
|
|
1735
|
+
${result.summary}
|
|
1736
|
+
`));
|
|
1737
|
+
}
|
|
1738
|
+
await memory.close();
|
|
1739
|
+
process.exit(result.exitCode);
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
console.error(errorRed2("Error:"), error instanceof Error ? error.message : String(error));
|
|
1742
|
+
process.exit(1);
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
program.command("consolidate").description("Consolidate memory: remove decayed entries and merge duplicates").addHelpText(
|
|
1746
|
+
"after",
|
|
1747
|
+
`
|
|
1748
|
+
Examples:
|
|
1749
|
+
$ sparn consolidate # Run memory consolidation
|
|
1750
|
+
|
|
1751
|
+
What It Does:
|
|
1752
|
+
1. Remove Decayed Entries: Deletes entries that have fully decayed
|
|
1753
|
+
2. Merge Duplicates: Combines identical content entries
|
|
1754
|
+
3. VACUUM Database: Reclaims disk space from deletions
|
|
1755
|
+
4. Update Statistics: Tracks consolidation metrics
|
|
1756
|
+
|
|
1757
|
+
When to Run:
|
|
1758
|
+
\u2022 After long-running sessions
|
|
1759
|
+
\u2022 Before important optimizations
|
|
1760
|
+
\u2022 When database size grows large
|
|
1761
|
+
\u2022 As part of nightly maintenance
|
|
1762
|
+
|
|
1763
|
+
Typical Results:
|
|
1764
|
+
\u2022 20-40% database size reduction
|
|
1765
|
+
\u2022 Faster query performance
|
|
1766
|
+
\u2022 Cleaner memory organization
|
|
1767
|
+
`
|
|
1768
|
+
).action(async () => {
|
|
1769
|
+
const { createKVMemory: createKVMemory2 } = await Promise.resolve().then(() => (init_kv_memory(), kv_memory_exports));
|
|
1770
|
+
const { consolidateCommand: consolidateCommand2 } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
|
|
1771
|
+
const { createConsolidateSpinner: createConsolidateSpinner2, showConsolidationSummary: showConsolidationSummary2 } = await Promise.resolve().then(() => (init_progress(), progress_exports));
|
|
1772
|
+
const { neuralCyan: neuralCyan2, synapseViolet: synapseViolet2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1773
|
+
const spinner = createConsolidateSpinner2("\u{1F9F9} Initializing memory consolidation...");
|
|
1774
|
+
try {
|
|
1775
|
+
spinner.start();
|
|
1776
|
+
spinner.text = "\u{1F4BE} Loading memory database...";
|
|
1777
|
+
const dbPath = resolve(process.cwd(), ".sparn/memory.db");
|
|
1778
|
+
const memory = await createKVMemory2(dbPath);
|
|
1779
|
+
spinner.text = "\u{1F50D} Identifying decayed entries...";
|
|
1780
|
+
const result = await consolidateCommand2({ memory });
|
|
1781
|
+
spinner.succeed(neuralCyan2(`Consolidation complete in ${result.durationMs}ms!`));
|
|
1782
|
+
showConsolidationSummary2(
|
|
1783
|
+
result.entriesBefore,
|
|
1784
|
+
result.entriesAfter,
|
|
1785
|
+
result.decayedRemoved,
|
|
1786
|
+
result.duplicatesRemoved,
|
|
1787
|
+
result.durationMs
|
|
1788
|
+
);
|
|
1789
|
+
if (result.vacuumCompleted) {
|
|
1790
|
+
console.log(synapseViolet2(" \u2713 Database VACUUM completed\n"));
|
|
1791
|
+
} else {
|
|
1792
|
+
console.log(errorRed2(" \u2717 Database VACUUM failed\n"));
|
|
1793
|
+
}
|
|
1794
|
+
await memory.close();
|
|
1795
|
+
} catch (error) {
|
|
1796
|
+
spinner.fail(errorRed2("Consolidation failed"));
|
|
1797
|
+
console.error(errorRed2("Error:"), error instanceof Error ? error.message : String(error));
|
|
1798
|
+
process.exit(1);
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
program.command("config [subcommand] [key] [value]").description("View or modify configuration").option("--json", "Output result as JSON").addHelpText(
|
|
1802
|
+
"after",
|
|
1803
|
+
`
|
|
1804
|
+
Examples:
|
|
1805
|
+
$ sparn config # Open config in $EDITOR
|
|
1806
|
+
$ sparn config get pruning.threshold # Get specific value
|
|
1807
|
+
$ sparn config set pruning.threshold 3 # Set value
|
|
1808
|
+
$ sparn config --json # View full config as JSON
|
|
1809
|
+
|
|
1810
|
+
Configuration Keys:
|
|
1811
|
+
pruning.threshold # Sparse coding threshold (2-5%)
|
|
1812
|
+
decay.halfLife # Engram decay half-life (hours)
|
|
1813
|
+
decay.minScore # Minimum decay score (0.0-1.0)
|
|
1814
|
+
states.activeThreshold # Active state threshold
|
|
1815
|
+
states.readyThreshold # Ready state threshold
|
|
1816
|
+
embedding.model # BTSP embedding model
|
|
1817
|
+
embedding.dimensions # Embedding vector size
|
|
1818
|
+
|
|
1819
|
+
The config file is located at .sparn/config.yaml
|
|
1820
|
+
`
|
|
1821
|
+
).action(async (subcommand, key, value, options) => {
|
|
1822
|
+
const { configCommand: configCommand2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
1823
|
+
const { neuralCyan: neuralCyan2, errorRed: errorRed2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
|
|
1824
|
+
try {
|
|
1825
|
+
const configPath = resolve(process.cwd(), ".sparn/config.yaml");
|
|
1826
|
+
const result = await configCommand2({
|
|
1827
|
+
configPath,
|
|
1828
|
+
subcommand,
|
|
1829
|
+
key,
|
|
1830
|
+
value,
|
|
1831
|
+
json: options.json || false
|
|
1832
|
+
});
|
|
1833
|
+
if (!result.success) {
|
|
1834
|
+
console.error(errorRed2("Error:"), result.error);
|
|
1835
|
+
process.exit(1);
|
|
1836
|
+
}
|
|
1837
|
+
if (result.editorPath && !options.json) {
|
|
1838
|
+
const editor = process.env["EDITOR"] || "vim";
|
|
1839
|
+
console.log(neuralCyan2(`
|
|
1840
|
+
\u{1F4DD} Opening config in ${editor}...
|
|
1841
|
+
`));
|
|
1842
|
+
const child = spawn2(editor, [result.editorPath], {
|
|
1843
|
+
stdio: "inherit"
|
|
1844
|
+
});
|
|
1845
|
+
child.on("close", (code) => {
|
|
1846
|
+
if (code === 0) {
|
|
1847
|
+
console.log(neuralCyan2("\n\u2713 Config edited\n"));
|
|
1848
|
+
}
|
|
1849
|
+
process.exit(code ?? 0);
|
|
1850
|
+
});
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
if (result.json) {
|
|
1854
|
+
console.log(result.json);
|
|
1855
|
+
} else if (result.message) {
|
|
1856
|
+
console.log(result.message);
|
|
1857
|
+
}
|
|
1858
|
+
} catch (error) {
|
|
1859
|
+
console.error(errorRed2("Error:"), error instanceof Error ? error.message : String(error));
|
|
1860
|
+
process.exit(1);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
program.on("option:version", () => {
|
|
1864
|
+
console.log(getBanner(VERSION2));
|
|
1865
|
+
process.exit(0);
|
|
1866
|
+
});
|
|
1867
|
+
program.parse();
|
|
1868
|
+
//# sourceMappingURL=index.js.map
|