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