@aiplumber/session-recall 1.9.0 → 1.9.2

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/session-recall +29 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiplumber/session-recall",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "Pull context from previous Claude Code sessions. Sessions end, context resets - this tool lets you continue where you left off.",
5
5
  "bin": {
6
6
  "session-recall": "./session-recall"
package/session-recall CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const VERSION = '1.9.0';
3
+ const VERSION = '1.9.2';
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
@@ -121,8 +121,8 @@ async function streamScanCompactions(filePath) {
121
121
 
122
122
  rl.on('line', (line) => {
123
123
  lineNum++;
124
- // Quick string check before parsing
125
- if (!line.includes('SessionStart:compact')) return;
124
+ // Quick string check before parsing - check both old and new formats
125
+ if (!line.includes('SessionStart:compact') && !line.includes('compact_boundary')) return;
126
126
 
127
127
  try {
128
128
  const msg = JSON.parse(line);
@@ -1293,9 +1293,16 @@ async function cmdLast(arg1, arg2, opts) {
1293
1293
  const candidateCount = Math.min(allSessions.length, n + 5);
1294
1294
  for (let i = 0; i < candidateCount; i++) {
1295
1295
  const s = allSessions[i];
1296
- // Use streaming for large files, sync for small files
1297
1296
  const stats = fs.statSync(s.filePath);
1298
1297
  const sizeMB = stats.size / (1024 * 1024);
1298
+
1299
+ // If we need compaction markers (for --before-compaction or --compactions),
1300
+ // scan for them FIRST before loading messages (since streaming filters them out)
1301
+ if ((opts.beforeCompaction || opts.compactionPhases) && sizeMB > 100) {
1302
+ s.compactionMarkers = await streamScanCompactions(s.filePath);
1303
+ }
1304
+
1305
+ // Use streaming for large files, sync for small files
1299
1306
  if (sizeMB > 100) {
1300
1307
  s.msgs = await streamReadJsonl(s.filePath);
1301
1308
  } else {
@@ -1330,7 +1337,8 @@ async function cmdLast(arg1, arg2, opts) {
1330
1337
  // Handle --compactions mode: show last N compaction phases instead of sessions
1331
1338
  if (opts.compactionPhases) {
1332
1339
  const session = selected[0]; // Use only the most recent session
1333
- const compactions = scanCompactionMarkers(session.msgs);
1340
+ // Use pre-scanned markers if available (for large files), otherwise scan now
1341
+ const compactions = session.compactionMarkers || scanCompactionMarkers(session.msgs);
1334
1342
 
1335
1343
  if (compactions.length === 0) {
1336
1344
  console.log('[session-recall v' + VERSION + '] No compaction events found - showing full session as single phase');
@@ -1545,7 +1553,8 @@ async function cmdLast(arg1, arg2, opts) {
1545
1553
  }
1546
1554
  // Apply before-compaction filter for dry-run
1547
1555
  if (opts.beforeCompaction && selected.length > 0) {
1548
- const compactions = scanCompactionMarkers(selected[0].msgs);
1556
+ // Use pre-scanned markers if available (for large files), otherwise scan now
1557
+ const compactions = selected[0].compactionMarkers || scanCompactionMarkers(selected[0].msgs);
1549
1558
  if (compactions.length > 0) {
1550
1559
  const n = opts.beforeCompactionN || compactions.length;
1551
1560
  if (n < 1 || n > compactions.length) {
@@ -1627,7 +1636,8 @@ async function cmdLast(arg1, arg2, opts) {
1627
1636
 
1628
1637
  // Filter to only show messages BEFORE the selected compaction boundary
1629
1638
  if (opts.beforeCompaction) {
1630
- const compactions = scanCompactionMarkers(session.msgs);
1639
+ // Use pre-scanned markers if available (for large files), otherwise scan now
1640
+ const compactions = session.compactionMarkers || scanCompactionMarkers(session.msgs);
1631
1641
  if (compactions.length > 0) {
1632
1642
  const n = opts.beforeCompactionN || compactions.length;
1633
1643
  if (n < 1 || n > compactions.length) {
@@ -2394,12 +2404,19 @@ function scanSessionTangents(filePath) {
2394
2404
  // ========== COMPACTION DETECTION ==========
2395
2405
 
2396
2406
  // Check if a message is a compaction marker
2407
+ // Supports both old format (progress + hook) and new format (system + subtype)
2397
2408
  function isCompactionMarker(msg) {
2398
- if (msg.type !== 'progress') return false;
2399
- if (!msg.data) return false;
2400
- return msg.data.type === 'hook_progress' &&
2401
- msg.data.hookEvent === 'SessionStart' &&
2402
- msg.data.hookName === 'SessionStart:compact';
2409
+ // New format (Claude Code 2.1+): system message with compact_boundary subtype
2410
+ if (msg.type === 'system' && msg.subtype === 'compact_boundary') {
2411
+ return true;
2412
+ }
2413
+ // Old format: progress message with hook
2414
+ if (msg.type === 'progress' && msg.data) {
2415
+ return msg.data.type === 'hook_progress' &&
2416
+ msg.data.hookEvent === 'SessionStart' &&
2417
+ msg.data.hookName === 'SessionStart:compact';
2418
+ }
2419
+ return false;
2403
2420
  }
2404
2421
 
2405
2422
  // Scan session for compaction markers with deduplication