@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,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