pi-goosedump 0.3.0 → 0.3.1

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/index.ts +87 -8
  2. package/package.json +2 -2
package/index.ts CHANGED
@@ -376,24 +376,103 @@ function buildCompactRange(
376
376
  : { scope: 'lineage', until: firstKeptEntryId };
377
377
  }
378
378
 
379
+ const GOOSEDUMP_NOTE =
380
+ 'Use `goosedump` to search prior active-lineage work, decisions, and context from before this summary. Do not redo work already completed.';
381
+ const GOOSEDUMP_SEPARATOR = '\n\n---\n\n';
382
+ const GOOSEDUMP_HEADERS = [
383
+ 'Session Goal',
384
+ 'Files And Changes',
385
+ 'Commits',
386
+ 'Outstanding Context',
387
+ 'User Preferences',
388
+ ] as const;
389
+
390
+ function isGoosedumpSection(value: string): boolean {
391
+ return GOOSEDUMP_HEADERS.some((header) => value.startsWith(`[${header}]`));
392
+ }
393
+
394
+ function stripGoosedumpRecallNote(summary: string): string {
395
+ const idx = summary.lastIndexOf(GOOSEDUMP_NOTE);
396
+ if (idx < 0) return summary.trim();
397
+ return summary
398
+ .slice(0, idx)
399
+ .replace(/\s*(?:\n\n---\n\n)?\s*$/, '')
400
+ .trim();
401
+ }
402
+
403
+ function sectionOf(summary: string, header: string): string {
404
+ const tag = `[${header}]`;
405
+ const start = summary.indexOf(tag);
406
+ if (start < 0) return '';
407
+
408
+ const rest = summary.slice(start);
409
+ const nextHeader = GOOSEDUMP_HEADERS.filter((h) => h !== header)
410
+ .map((h) => rest.indexOf(`[${h}]`))
411
+ .filter((idx) => idx > 0);
412
+ const nextSeparator = rest.indexOf(GOOSEDUMP_SEPARATOR);
413
+ const candidates = [...nextHeader, ...(nextSeparator > 0 ? [nextSeparator] : [])].sort(
414
+ (a, b) => a - b,
415
+ );
416
+ const end = candidates[0];
417
+ return (end ? rest.slice(0, end) : rest).trim();
418
+ }
419
+
420
+ function briefOf(summary: string): string {
421
+ return stripGoosedumpRecallNote(summary)
422
+ .split(GOOSEDUMP_SEPARATOR)
423
+ .map((part) => part.trim())
424
+ .filter((part) => part && !isGoosedumpSection(part))
425
+ .join(GOOSEDUMP_SEPARATOR);
426
+ }
427
+
428
+ function mergeSection(header: string, previous: string, next: string): string {
429
+ if (header === 'Outstanding Context') return next;
430
+ if (!previous) return next;
431
+ if (!next) return previous;
432
+
433
+ const lines = [
434
+ ...new Set(
435
+ [...previous.split('\n'), ...next.split('\n')].filter((line) => line.startsWith('- ')),
436
+ ),
437
+ ];
438
+ const cap = header === 'Session Goal' ? 8 : header === 'User Preferences' ? 15 : 10;
439
+ const capped = lines.length > cap ? lines.slice(-cap) : lines;
440
+ return capped.length > 0 ? `[${header}]\n${capped.join('\n')}` : '';
441
+ }
442
+
443
+ function capBriefTranscript(brief: string): string {
444
+ const lines = brief.split('\n');
445
+ if (lines.length <= 120) return brief;
446
+ const kept = lines.slice(-120);
447
+ const firstHeader = kept.findIndex((line) => /^\[(user|assistant)\]$/.test(line));
448
+ const clean = firstHeader > 0 ? kept.slice(firstHeader) : kept;
449
+ return `...(${lines.length - clean.length} earlier lines omitted)\n\n${clean.join('\n')}`;
450
+ }
451
+
379
452
  function mergeWithPreviousSummary(
380
453
  summary: string,
381
454
  previousSummary: string | undefined,
382
455
  ): { summary: string; previousSummaryIncluded: boolean } {
383
- const nextSummary = summary.trim();
384
- const priorSummary = previousSummary?.trim();
456
+ const nextSummary = stripGoosedumpRecallNote(summary);
457
+ const priorSummary = previousSummary ? stripGoosedumpRecallNote(previousSummary) : '';
385
458
 
386
459
  if (!priorSummary) {
387
- return { summary: nextSummary, previousSummaryIncluded: false };
460
+ return { summary: summary.trim(), previousSummaryIncluded: false };
388
461
  }
389
462
  if (!nextSummary) {
390
- return { summary: priorSummary, previousSummaryIncluded: true };
463
+ return { summary: previousSummary?.trim() ?? '', previousSummaryIncluded: true };
391
464
  }
392
465
 
393
- return {
394
- summary: `${priorSummary}\n\n## Recent Compaction Update\n${nextSummary}`,
395
- previousSummaryIncluded: true,
396
- };
466
+ const sections = GOOSEDUMP_HEADERS.map((header) =>
467
+ mergeSection(header, sectionOf(priorSummary, header), sectionOf(nextSummary, header)),
468
+ ).filter(Boolean);
469
+ const brief = capBriefTranscript(
470
+ [briefOf(priorSummary), briefOf(nextSummary)].filter(Boolean).join('\n\n'),
471
+ );
472
+ const parts = [...sections, ...(brief ? [brief] : [])];
473
+ const merged = `${parts.join(GOOSEDUMP_SEPARATOR)}${GOOSEDUMP_SEPARATOR}${GOOSEDUMP_NOTE}`;
474
+
475
+ return { summary: merged.trim(), previousSummaryIncluded: true };
397
476
  }
398
477
 
399
478
  function buildGoosedumpCompaction(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goosedump",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Coding agent context data browser plugin for pi",
5
5
  "keywords": [
6
6
  "goosedump",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@earendil-works/pi-tui": "^0.78.0",
32
- "@jarkkojs/goosedump": "^0.3.1",
32
+ "@jarkkojs/goosedump": "^0.3.2",
33
33
  "@sinclair/typebox": "^0.34.49"
34
34
  },
35
35
  "devDependencies": {