pi-goosedump 0.4.0 → 0.5.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.
- package/index.ts +261 -260
- package/package.json +2 -2
package/index.ts
CHANGED
|
@@ -23,10 +23,11 @@ import { Type } from '@sinclair/typebox';
|
|
|
23
23
|
|
|
24
24
|
const require = createRequire(import.meta.url);
|
|
25
25
|
|
|
26
|
-
const GOOSEDUMP_VERSION = [0,
|
|
26
|
+
const GOOSEDUMP_VERSION = [0, 5, 0] as const;
|
|
27
27
|
|
|
28
28
|
interface GoosedumpListing {
|
|
29
29
|
id: string;
|
|
30
|
+
path: string | null;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
interface GoosedumpMessage {
|
|
@@ -36,15 +37,14 @@ interface GoosedumpMessage {
|
|
|
36
37
|
score?: number;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
interface
|
|
40
|
+
interface GoosedumpSearchResult {
|
|
40
41
|
messages: GoosedumpMessage[];
|
|
41
|
-
totalCount: number;
|
|
42
42
|
page: number;
|
|
43
43
|
totalPages: number;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
interface ListJson {
|
|
47
|
-
sessions: { id: string }[];
|
|
47
|
+
sessions: { id: string; path?: string | null }[];
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
interface ToolCallJson {
|
|
@@ -86,10 +86,6 @@ interface SearchJson {
|
|
|
86
86
|
hits: HitJson[];
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
interface GrepJson {
|
|
90
|
-
hits: HitJson[];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
89
|
interface CompactJson {
|
|
94
90
|
goals: string[];
|
|
95
91
|
files: { modified: string[]; created: string[]; read: string[] };
|
|
@@ -232,6 +228,14 @@ function formatMessagesFull(messages: GoosedumpMessage[]): string {
|
|
|
232
228
|
return messages.map((m) => `[${m.id}] ${m.role}:\n${m.content}`).join('\n\n---\n\n');
|
|
233
229
|
}
|
|
234
230
|
|
|
231
|
+
function textResult(text: string): AgentToolResult<void> {
|
|
232
|
+
return { content: [{ type: 'text', text }], details: undefined };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function missingContextId(action: string): AgentToolResult<void> {
|
|
236
|
+
return textResult(`contextId is required for ${action} when no current session is available`);
|
|
237
|
+
}
|
|
238
|
+
|
|
235
239
|
function runGoosedump(args: string[]): string {
|
|
236
240
|
return execFileSync(process.execPath, [binaryPath(), ...args], {
|
|
237
241
|
encoding: 'utf-8',
|
|
@@ -240,62 +244,37 @@ function runGoosedump(args: string[]): string {
|
|
|
240
244
|
|
|
241
245
|
function goosedumpList(): GoosedumpListing[] {
|
|
242
246
|
const json = JSON.parse(runGoosedump(['list', 'pi:*'])) as ListJson;
|
|
243
|
-
return (json.sessions ?? []).map((session) => ({
|
|
247
|
+
return (json.sessions ?? []).map((session) => ({
|
|
248
|
+
id: session.id,
|
|
249
|
+
path: session.path ?? null,
|
|
250
|
+
}));
|
|
244
251
|
}
|
|
245
252
|
|
|
246
|
-
function
|
|
253
|
+
function goosedumpSearch(
|
|
247
254
|
contextId: string,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
ids?: string;
|
|
252
|
-
rank?: boolean;
|
|
253
|
-
page?: number;
|
|
254
|
-
} = {},
|
|
255
|
-
): GoosedumpContextResult {
|
|
255
|
+
query: string,
|
|
256
|
+
options: { scope?: string; page?: number } = {},
|
|
257
|
+
): GoosedumpSearchResult {
|
|
256
258
|
const scope = options.scope ?? 'lineage';
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
const target = `pi:${contextId}`;
|
|
260
|
-
const args = hasQuery
|
|
261
|
-
? isSearch
|
|
262
|
-
? ['search', target, options.grep ?? '']
|
|
263
|
-
: ['grep', target, options.grep ?? '']
|
|
264
|
-
: ['show', target];
|
|
265
|
-
|
|
266
|
-
if (options.ids) {
|
|
267
|
-
args.push('--ids', options.ids);
|
|
268
|
-
}
|
|
269
|
-
if (isSearch && options.page) {
|
|
270
|
-
args.push('--page', String(options.page));
|
|
271
|
-
}
|
|
259
|
+
const args = ['search', `pi:${contextId}`, query];
|
|
260
|
+
if (options.page) args.push('--page', String(options.page));
|
|
272
261
|
args.push('--scope', scope);
|
|
273
262
|
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const json = JSON.parse(output) as SearchJson | GrepJson;
|
|
278
|
-
const messages = (json.hits ?? []).map(hitMessage);
|
|
279
|
-
const page = isSearch ? ((json as SearchJson).page ?? options.page ?? 1) : 1;
|
|
280
|
-
const totalPages = isSearch ? ((json as SearchJson).totalPages ?? 1) : 1;
|
|
281
|
-
return { messages, totalCount: messages.length, page, totalPages };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const json = JSON.parse(output) as ShowJson;
|
|
285
|
-
const messages = (json.messages ?? []).map(showMessage);
|
|
286
|
-
return { messages, totalCount: messages.length, page: options.page ?? 1, totalPages: 1 };
|
|
263
|
+
const json = JSON.parse(runGoosedump(args)) as SearchJson;
|
|
264
|
+
const messages = (json.hits ?? []).map(hitMessage);
|
|
265
|
+
return { messages, page: json.page ?? options.page ?? 1, totalPages: json.totalPages ?? 1 };
|
|
287
266
|
}
|
|
288
267
|
|
|
289
|
-
function
|
|
268
|
+
function goosedumpShow(
|
|
290
269
|
contextId: string,
|
|
291
|
-
|
|
292
|
-
scope: string = 'lineage',
|
|
270
|
+
options: { scope?: string; ids?: string[] } = {},
|
|
293
271
|
): GoosedumpMessage[] {
|
|
294
|
-
|
|
272
|
+
const scope = options.scope ?? 'lineage';
|
|
273
|
+
const args = ['show', `pi:${contextId}`];
|
|
274
|
+
if (options.ids && options.ids.length > 0) args.push('--ids', options.ids.join(','));
|
|
275
|
+
args.push('--scope', scope);
|
|
295
276
|
|
|
296
|
-
const json = JSON.parse(
|
|
297
|
-
runGoosedump(['show', `pi:${contextId}`, '--ids', entryIds.join(','), '--scope', scope]),
|
|
298
|
-
) as ShowJson;
|
|
277
|
+
const json = JSON.parse(runGoosedump(args)) as ShowJson;
|
|
299
278
|
return (json.messages ?? []).map(showMessage);
|
|
300
279
|
}
|
|
301
280
|
|
|
@@ -588,14 +567,204 @@ function buildGoosedumpCompaction(
|
|
|
588
567
|
};
|
|
589
568
|
}
|
|
590
569
|
|
|
570
|
+
async function runSessionBrowser(
|
|
571
|
+
ctx: ExtensionContext,
|
|
572
|
+
listings: GoosedumpListing[],
|
|
573
|
+
): Promise<string | null | undefined> {
|
|
574
|
+
let resumePath: string | null | undefined;
|
|
575
|
+
|
|
576
|
+
await ctx.ui.custom<void>(
|
|
577
|
+
(tui, theme, _kb, done) => {
|
|
578
|
+
let selectedIndex = 0;
|
|
579
|
+
let mode: 'list' | 'compact' = 'list';
|
|
580
|
+
let compactLines: string[] = [];
|
|
581
|
+
let compactScroll = 0;
|
|
582
|
+
let compactTitle = '';
|
|
583
|
+
|
|
584
|
+
const COMPACT_VIEWPORT = 20;
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
render(width: number): string[] {
|
|
588
|
+
const innerW = width - 4;
|
|
589
|
+
const border = theme.fg('border', '│');
|
|
590
|
+
const borderFg = (s: string) => theme.fg('border', s);
|
|
591
|
+
const dim = (s: string) => theme.fg('dim', s);
|
|
592
|
+
const accent = (s: string) => theme.fg('accent', s);
|
|
593
|
+
const text = (s: string) => theme.fg('text', s);
|
|
594
|
+
const muted = (s: string) => theme.fg('muted', s);
|
|
595
|
+
|
|
596
|
+
const lines: string[] = [];
|
|
597
|
+
|
|
598
|
+
if (mode === 'compact') {
|
|
599
|
+
const title = accent(` Compact: ${truncateToWidth(compactTitle, 40)} `);
|
|
600
|
+
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
601
|
+
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
602
|
+
|
|
603
|
+
const total = compactLines.length;
|
|
604
|
+
const maxScroll = Math.max(0, total - COMPACT_VIEWPORT);
|
|
605
|
+
if (compactScroll > maxScroll) compactScroll = maxScroll;
|
|
606
|
+
const window = compactLines.slice(compactScroll, compactScroll + COMPACT_VIEWPORT);
|
|
607
|
+
for (const row of window) {
|
|
608
|
+
lines.push(makeRow(text(truncateToWidth(row, innerW)), innerW, border));
|
|
609
|
+
}
|
|
610
|
+
for (let i = window.length; i < COMPACT_VIEWPORT; i++) {
|
|
611
|
+
lines.push(makeRow('', innerW, border));
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const position =
|
|
615
|
+
total > COMPACT_VIEWPORT
|
|
616
|
+
? `${compactScroll + 1}-${Math.min(compactScroll + COMPACT_VIEWPORT, total)} of ${total}`
|
|
617
|
+
: `${total} line${total === 1 ? '' : 's'}`;
|
|
618
|
+
lines.push(makeRow('', innerW, border));
|
|
619
|
+
lines.push(makeRow(dim(`↑↓ scroll esc back (${position})`), innerW, border));
|
|
620
|
+
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
621
|
+
return lines;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Top border
|
|
625
|
+
const title = accent(' Goosedump Sessions ');
|
|
626
|
+
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
627
|
+
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
628
|
+
|
|
629
|
+
// Session count
|
|
630
|
+
const countInfo = `${listings.length} session${listings.length === 1 ? '' : 's'}`;
|
|
631
|
+
lines.push(makeRow(`${muted(countInfo)}`, innerW, border));
|
|
632
|
+
|
|
633
|
+
lines.push(makeRow('', innerW, border));
|
|
634
|
+
|
|
635
|
+
// Session list
|
|
636
|
+
const maxItems = Math.min(listings.length, 20);
|
|
637
|
+
for (let i = 0; i < maxItems; i++) {
|
|
638
|
+
const listing = listings[i];
|
|
639
|
+
const isSelected = i === selectedIndex;
|
|
640
|
+
const cursor = isSelected ? accent('▶') : ' ';
|
|
641
|
+
const idText = isSelected ? accent(listing.id) : text(listing.id);
|
|
642
|
+
const line = ` ${cursor} ${idText}`;
|
|
643
|
+
lines.push(makeRow(truncateToWidth(line, innerW), innerW, border));
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (listings.length > 20) {
|
|
647
|
+
lines.push(makeRow(dim(` ... and ${listings.length - 20} more`), innerW, border));
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Footer
|
|
651
|
+
lines.push(makeRow('', innerW, border));
|
|
652
|
+
lines.push(
|
|
653
|
+
makeRow(
|
|
654
|
+
dim('↑↓ navigate enter compact shift+enter resume esc close'),
|
|
655
|
+
innerW,
|
|
656
|
+
border,
|
|
657
|
+
),
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
// Bottom border
|
|
661
|
+
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
662
|
+
|
|
663
|
+
return lines;
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
handleInput(data: string): void {
|
|
667
|
+
if (mode === 'compact') {
|
|
668
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
669
|
+
mode = 'list';
|
|
670
|
+
tui.requestRender();
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (matchesKey(data, Key.up)) {
|
|
674
|
+
compactScroll = Math.max(0, compactScroll - 1);
|
|
675
|
+
tui.requestRender();
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (matchesKey(data, Key.down)) {
|
|
679
|
+
compactScroll += 1;
|
|
680
|
+
tui.requestRender();
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (matchesKey(data, Key.pageUp)) {
|
|
684
|
+
compactScroll = Math.max(0, compactScroll - COMPACT_VIEWPORT);
|
|
685
|
+
tui.requestRender();
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
if (matchesKey(data, Key.pageDown)) {
|
|
689
|
+
compactScroll += COMPACT_VIEWPORT;
|
|
690
|
+
tui.requestRender();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
697
|
+
done(undefined);
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (matchesKey(data, Key.up)) {
|
|
702
|
+
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
703
|
+
tui.requestRender();
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (matchesKey(data, Key.down)) {
|
|
708
|
+
selectedIndex = Math.min(listings.length - 1, selectedIndex + 1);
|
|
709
|
+
tui.requestRender();
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (matchesKey(data, Key.shift(Key.enter))) {
|
|
714
|
+
resumePath = listings[selectedIndex]?.path;
|
|
715
|
+
done(undefined);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (matchesKey(data, Key.enter) || matchesKey(data, Key.return)) {
|
|
720
|
+
const listing = listings[selectedIndex];
|
|
721
|
+
if (!listing) return;
|
|
722
|
+
compactTitle = listing.id;
|
|
723
|
+
compactScroll = 0;
|
|
724
|
+
try {
|
|
725
|
+
const summary = goosedumpCompact(listing.id, { scope: 'lineage' }).trim();
|
|
726
|
+
compactLines = summary
|
|
727
|
+
? summary.split('\n')
|
|
728
|
+
: ['No compact summary available for this session.'];
|
|
729
|
+
} catch (err) {
|
|
730
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
731
|
+
compactLines = [`Failed to compact session: ${msg}`];
|
|
732
|
+
}
|
|
733
|
+
mode = 'compact';
|
|
734
|
+
tui.requestRender();
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
|
|
739
|
+
invalidate(): void {},
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
function makeRow(content: string, innerW: number, border: string): string {
|
|
743
|
+
const line = truncateToWidth(content, innerW);
|
|
744
|
+
const pad = Math.max(0, innerW - visibleWidth(line));
|
|
745
|
+
return `${border} ${line}${' '.repeat(pad)} ${border}`;
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
overlay: true,
|
|
750
|
+
overlayOptions: {
|
|
751
|
+
anchor: 'center',
|
|
752
|
+
width: 72,
|
|
753
|
+
margin: 2,
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
return resumePath;
|
|
759
|
+
}
|
|
760
|
+
|
|
591
761
|
export function createGoosedumpIntegration(
|
|
592
762
|
options: GoosedumpIntegrationOptions = {},
|
|
593
763
|
): GoosedumpIntegration {
|
|
594
764
|
const shouldRegisterTool = options.registerTool ?? true;
|
|
595
765
|
const shouldOverrideDefaultCompaction = options.overrideDefaultCompaction ?? true;
|
|
596
766
|
|
|
597
|
-
let
|
|
598
|
-
let goosedumpEnabled = false;
|
|
767
|
+
let goosedumpAvailable = false;
|
|
599
768
|
|
|
600
769
|
function checkGoosedump(ctx?: ExtensionContext): boolean {
|
|
601
770
|
const version = goosedumpVersion();
|
|
@@ -686,16 +855,10 @@ export function createGoosedumpIntegration(
|
|
|
686
855
|
),
|
|
687
856
|
}),
|
|
688
857
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
689
|
-
if (!
|
|
690
|
-
return
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
type: 'text',
|
|
694
|
-
text: 'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
695
|
-
},
|
|
696
|
-
],
|
|
697
|
-
details: undefined,
|
|
698
|
-
};
|
|
858
|
+
if (!goosedumpAvailable) {
|
|
859
|
+
return textResult(
|
|
860
|
+
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
861
|
+
);
|
|
699
862
|
}
|
|
700
863
|
|
|
701
864
|
try {
|
|
@@ -703,10 +866,7 @@ export function createGoosedumpIntegration(
|
|
|
703
866
|
case 'list': {
|
|
704
867
|
const listings = goosedumpList();
|
|
705
868
|
if (listings.length === 0) {
|
|
706
|
-
return
|
|
707
|
-
content: [{ type: 'text', text: 'No sessions found.' }],
|
|
708
|
-
details: undefined,
|
|
709
|
-
};
|
|
869
|
+
return textResult('No sessions found.');
|
|
710
870
|
}
|
|
711
871
|
|
|
712
872
|
const lines = ['Available sessions:', ''];
|
|
@@ -714,45 +874,22 @@ export function createGoosedumpIntegration(
|
|
|
714
874
|
lines.push(` ${listing.id}`);
|
|
715
875
|
}
|
|
716
876
|
|
|
717
|
-
return
|
|
718
|
-
content: [{ type: 'text', text: lines.join('\n') }],
|
|
719
|
-
details: undefined,
|
|
720
|
-
};
|
|
877
|
+
return textResult(lines.join('\n'));
|
|
721
878
|
}
|
|
722
879
|
|
|
723
880
|
case 'search': {
|
|
724
881
|
const contextId = params.contextId ?? currentSessionContextId(ctx);
|
|
725
|
-
if (!contextId)
|
|
726
|
-
return {
|
|
727
|
-
content: [
|
|
728
|
-
{
|
|
729
|
-
type: 'text',
|
|
730
|
-
text: 'contextId is required for search when no current session is available',
|
|
731
|
-
},
|
|
732
|
-
],
|
|
733
|
-
details: undefined,
|
|
734
|
-
};
|
|
735
|
-
}
|
|
882
|
+
if (!contextId) return missingContextId('search');
|
|
736
883
|
|
|
737
|
-
if (!params.query)
|
|
738
|
-
return {
|
|
739
|
-
content: [{ type: 'text', text: 'query is required for search' }],
|
|
740
|
-
details: undefined,
|
|
741
|
-
};
|
|
742
|
-
}
|
|
884
|
+
if (!params.query) return textResult('query is required for search');
|
|
743
885
|
|
|
744
|
-
const result =
|
|
886
|
+
const result = goosedumpSearch(contextId, params.query, {
|
|
745
887
|
scope: params.scope ?? 'lineage',
|
|
746
|
-
grep: params.query,
|
|
747
|
-
rank: true,
|
|
748
888
|
page: params.page ?? 1,
|
|
749
889
|
});
|
|
750
890
|
|
|
751
891
|
if (result.messages.length === 0) {
|
|
752
|
-
return {
|
|
753
|
-
content: [{ type: 'text', text: `No results for "${params.query}"` }],
|
|
754
|
-
details: undefined,
|
|
755
|
-
};
|
|
892
|
+
return textResult(`No results for "${params.query}"`);
|
|
756
893
|
}
|
|
757
894
|
|
|
758
895
|
const useCompact = params.compact ?? true;
|
|
@@ -761,97 +898,48 @@ export function createGoosedumpIntegration(
|
|
|
761
898
|
: formatMessagesFull(result.messages);
|
|
762
899
|
|
|
763
900
|
const header = `Results for "${params.query}" (page ${result.page} of ${result.totalPages}):\n\n`;
|
|
764
|
-
return
|
|
765
|
-
content: [{ type: 'text', text: header + text }],
|
|
766
|
-
details: undefined,
|
|
767
|
-
};
|
|
901
|
+
return textResult(header + text);
|
|
768
902
|
}
|
|
769
903
|
|
|
770
904
|
case 'expand': {
|
|
771
905
|
const contextId = params.contextId ?? currentSessionContextId(ctx);
|
|
772
|
-
if (!contextId)
|
|
773
|
-
return {
|
|
774
|
-
content: [
|
|
775
|
-
{
|
|
776
|
-
type: 'text',
|
|
777
|
-
text: 'contextId is required for expand when no current session is available',
|
|
778
|
-
},
|
|
779
|
-
],
|
|
780
|
-
details: undefined,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
906
|
+
if (!contextId) return missingContextId('expand');
|
|
783
907
|
|
|
784
908
|
if (!params.ids || params.ids.length === 0) {
|
|
785
|
-
return
|
|
786
|
-
content: [
|
|
787
|
-
{ type: 'text', text: 'ids is required for expand (array of entry IDs)' },
|
|
788
|
-
],
|
|
789
|
-
details: undefined,
|
|
790
|
-
};
|
|
909
|
+
return textResult('ids is required for expand (array of entry IDs)');
|
|
791
910
|
}
|
|
792
911
|
|
|
793
|
-
const messages =
|
|
794
|
-
|
|
795
|
-
params.ids,
|
|
796
|
-
|
|
797
|
-
);
|
|
912
|
+
const messages = goosedumpShow(contextId, {
|
|
913
|
+
scope: params.scope ?? 'lineage',
|
|
914
|
+
ids: params.ids,
|
|
915
|
+
});
|
|
798
916
|
|
|
799
917
|
if (messages.length === 0) {
|
|
800
|
-
return
|
|
801
|
-
content: [{ type: 'text', text: 'No entries found for the given IDs.' }],
|
|
802
|
-
details: undefined,
|
|
803
|
-
};
|
|
918
|
+
return textResult('No entries found for the given IDs.');
|
|
804
919
|
}
|
|
805
920
|
|
|
806
|
-
return
|
|
807
|
-
content: [{ type: 'text', text: formatMessagesFull(messages) }],
|
|
808
|
-
details: undefined,
|
|
809
|
-
};
|
|
921
|
+
return textResult(formatMessagesFull(messages));
|
|
810
922
|
}
|
|
811
923
|
|
|
812
924
|
case 'view': {
|
|
813
925
|
const contextId = params.contextId ?? currentSessionContextId(ctx);
|
|
814
|
-
if (!contextId)
|
|
815
|
-
return {
|
|
816
|
-
content: [
|
|
817
|
-
{
|
|
818
|
-
type: 'text',
|
|
819
|
-
text: 'contextId is required for view when no current session is available',
|
|
820
|
-
},
|
|
821
|
-
],
|
|
822
|
-
details: undefined,
|
|
823
|
-
};
|
|
824
|
-
}
|
|
926
|
+
if (!contextId) return missingContextId('view');
|
|
825
927
|
|
|
826
|
-
const
|
|
827
|
-
scope: params.scope ?? 'lineage',
|
|
828
|
-
});
|
|
928
|
+
const messages = goosedumpShow(contextId, { scope: params.scope ?? 'lineage' });
|
|
829
929
|
|
|
830
|
-
if (
|
|
831
|
-
return {
|
|
832
|
-
content: [{ type: 'text', text: `Session "${contextId}" has no messages.` }],
|
|
833
|
-
details: undefined,
|
|
834
|
-
};
|
|
930
|
+
if (messages.length === 0) {
|
|
931
|
+
return textResult(`Session "${contextId}" has no messages.`);
|
|
835
932
|
}
|
|
836
933
|
|
|
837
|
-
return
|
|
838
|
-
content: [{ type: 'text', text: formatMessagesFull(result.messages) }],
|
|
839
|
-
details: undefined,
|
|
840
|
-
};
|
|
934
|
+
return textResult(formatMessagesFull(messages));
|
|
841
935
|
}
|
|
842
936
|
|
|
843
937
|
default:
|
|
844
|
-
return {
|
|
845
|
-
content: [{ type: 'text', text: `Unknown action: ${params.action}` }],
|
|
846
|
-
details: undefined,
|
|
847
|
-
};
|
|
938
|
+
return textResult(`Unknown action: ${params.action}`);
|
|
848
939
|
}
|
|
849
940
|
} catch (err) {
|
|
850
941
|
const message = err instanceof Error ? err.message : String(err);
|
|
851
|
-
return {
|
|
852
|
-
content: [{ type: 'text', text: `goosedump error: ${message}` }],
|
|
853
|
-
details: undefined,
|
|
854
|
-
};
|
|
942
|
+
return textResult(`goosedump error: ${message}`);
|
|
855
943
|
}
|
|
856
944
|
},
|
|
857
945
|
}),
|
|
@@ -859,10 +947,9 @@ export function createGoosedumpIntegration(
|
|
|
859
947
|
}
|
|
860
948
|
|
|
861
949
|
pi.on('session_start', async (_event, ctx) => {
|
|
862
|
-
|
|
863
|
-
goosedumpEnabled = goosedumpReady;
|
|
950
|
+
goosedumpAvailable = checkGoosedump(ctx);
|
|
864
951
|
|
|
865
|
-
if (
|
|
952
|
+
if (goosedumpAvailable) {
|
|
866
953
|
const theme = ctx.ui.theme;
|
|
867
954
|
const dot = theme.fg('success', '●');
|
|
868
955
|
const label = theme.fg('text', 'Goosedump');
|
|
@@ -871,7 +958,7 @@ export function createGoosedumpIntegration(
|
|
|
871
958
|
});
|
|
872
959
|
|
|
873
960
|
pi.on('session_before_compact', async (event, ctx) => {
|
|
874
|
-
if (!shouldOverrideDefaultCompaction || !
|
|
961
|
+
if (!shouldOverrideDefaultCompaction || !goosedumpAvailable) return;
|
|
875
962
|
if (event.customInstructions?.trim()) return;
|
|
876
963
|
if (event.signal.aborted) return;
|
|
877
964
|
|
|
@@ -901,7 +988,7 @@ export function createGoosedumpIntegration(
|
|
|
901
988
|
});
|
|
902
989
|
|
|
903
990
|
pi.on('session_compact', async (event, ctx) => {
|
|
904
|
-
if (!
|
|
991
|
+
if (!goosedumpAvailable || !event.fromExtension) return;
|
|
905
992
|
|
|
906
993
|
const compactionEntry = event.compactionEntry;
|
|
907
994
|
if (!compactionEntry) return;
|
|
@@ -925,7 +1012,7 @@ export function createGoosedumpIntegration(
|
|
|
925
1012
|
maybePi.registerCommand?.('goosedump', {
|
|
926
1013
|
description: 'Browse coding agent session history',
|
|
927
1014
|
handler: async (_args, ctx) => {
|
|
928
|
-
if (!
|
|
1015
|
+
if (!goosedumpAvailable) {
|
|
929
1016
|
ctx.ui.notify(
|
|
930
1017
|
'goosedump is not available. Install with: npm install @jarkkojs/goosedump',
|
|
931
1018
|
'error',
|
|
@@ -941,99 +1028,13 @@ export function createGoosedumpIntegration(
|
|
|
941
1028
|
return;
|
|
942
1029
|
}
|
|
943
1030
|
|
|
944
|
-
await ctx
|
|
945
|
-
(tui, theme, _kb, done) => {
|
|
946
|
-
let selectedIndex = 0;
|
|
947
|
-
|
|
948
|
-
return {
|
|
949
|
-
render(width: number): string[] {
|
|
950
|
-
const innerW = width - 4;
|
|
951
|
-
const border = theme.fg('border', '│');
|
|
952
|
-
const borderFg = (s: string) => theme.fg('border', s);
|
|
953
|
-
const dim = (s: string) => theme.fg('dim', s);
|
|
954
|
-
const accent = (s: string) => theme.fg('accent', s);
|
|
955
|
-
const text = (s: string) => theme.fg('text', s);
|
|
956
|
-
const muted = (s: string) => theme.fg('muted', s);
|
|
957
|
-
|
|
958
|
-
const lines: string[] = [];
|
|
959
|
-
|
|
960
|
-
// Top border
|
|
961
|
-
const title = accent(' Goosedump Sessions ');
|
|
962
|
-
const topFill = borderFg(
|
|
963
|
-
'─'.repeat(Math.max(0, width - 4 - visibleWidth(title))),
|
|
964
|
-
);
|
|
965
|
-
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
966
|
-
|
|
967
|
-
// Session count
|
|
968
|
-
const countInfo = `${listings.length} session${listings.length === 1 ? '' : 's'}`;
|
|
969
|
-
lines.push(makeRow(`${muted(countInfo)}`, innerW, border));
|
|
970
|
-
|
|
971
|
-
lines.push(makeRow('', innerW, border));
|
|
972
|
-
|
|
973
|
-
// Session list
|
|
974
|
-
const maxItems = Math.min(listings.length, 20);
|
|
975
|
-
for (let i = 0; i < maxItems; i++) {
|
|
976
|
-
const listing = listings[i];
|
|
977
|
-
const isSelected = i === selectedIndex;
|
|
978
|
-
const cursor = isSelected ? accent('▶') : ' ';
|
|
979
|
-
const idText = isSelected ? accent(listing.id) : text(listing.id);
|
|
980
|
-
const line = ` ${cursor} ${idText}`;
|
|
981
|
-
lines.push(makeRow(truncateToWidth(line, innerW), innerW, border));
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
if (listings.length > 20) {
|
|
985
|
-
lines.push(
|
|
986
|
-
makeRow(dim(` ... and ${listings.length - 20} more`), innerW, border),
|
|
987
|
-
);
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
// Footer
|
|
991
|
-
lines.push(makeRow('', innerW, border));
|
|
992
|
-
lines.push(makeRow(dim('↑↓ navigate esc close'), innerW, border));
|
|
993
|
-
|
|
994
|
-
// Bottom border
|
|
995
|
-
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
996
|
-
|
|
997
|
-
return lines;
|
|
998
|
-
},
|
|
1031
|
+
const resumePath = await runSessionBrowser(ctx, listings);
|
|
999
1032
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
if (matchesKey(data, Key.up)) {
|
|
1007
|
-
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
1008
|
-
tui.requestRender();
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (matchesKey(data, Key.down)) {
|
|
1013
|
-
selectedIndex = Math.min(listings.length - 1, selectedIndex + 1);
|
|
1014
|
-
tui.requestRender();
|
|
1015
|
-
return;
|
|
1016
|
-
}
|
|
1017
|
-
},
|
|
1018
|
-
|
|
1019
|
-
invalidate(): void {},
|
|
1020
|
-
};
|
|
1021
|
-
|
|
1022
|
-
function makeRow(content: string, innerW: number, border: string): string {
|
|
1023
|
-
const line = truncateToWidth(content, innerW);
|
|
1024
|
-
const pad = Math.max(0, innerW - visibleWidth(line));
|
|
1025
|
-
return `${border} ${line}${' '.repeat(pad)} ${border}`;
|
|
1026
|
-
}
|
|
1027
|
-
},
|
|
1028
|
-
{
|
|
1029
|
-
overlay: true,
|
|
1030
|
-
overlayOptions: {
|
|
1031
|
-
anchor: 'center',
|
|
1032
|
-
width: 72,
|
|
1033
|
-
margin: 2,
|
|
1034
|
-
},
|
|
1035
|
-
},
|
|
1036
|
-
);
|
|
1033
|
+
if (resumePath) {
|
|
1034
|
+
await ctx.switchSession(resumePath);
|
|
1035
|
+
} else if (resumePath === null) {
|
|
1036
|
+
ctx.ui.notify('Cannot resume: session file path is unavailable.', 'error');
|
|
1037
|
+
}
|
|
1037
1038
|
} catch (err) {
|
|
1038
1039
|
const message = err instanceof Error ? err.message : String(err);
|
|
1039
1040
|
ctx.ui.notify(`goosedump error: ${message}`, 'error');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
32
|
+
"@jarkkojs/goosedump": "^0.5.0",
|
|
33
33
|
"@sinclair/typebox": "^0.34.49"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|