pi-goosedump 0.5.6 → 0.6.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.
- package/README.md +29 -1
- package/index.ts +144 -30
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Once installed, pi-goosedump registers a tool and a slash command:
|
|
|
21
21
|
|
|
22
22
|
### Tool: `goosedump`
|
|
23
23
|
|
|
24
|
-
The agent can browse the current session by default or a named session with `contextId`:
|
|
24
|
+
The agent can browse the current Pi session by default or a named session with `contextId`:
|
|
25
25
|
|
|
26
26
|
| Action | Description |
|
|
27
27
|
| -------- | ---------------------------------------- |
|
|
@@ -42,6 +42,34 @@ goosedump({ action: "expand", ids: ["entry-a", "entry-b"] })
|
|
|
42
42
|
goosedump({ action: "view" })
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
#### Other providers
|
|
46
|
+
|
|
47
|
+
By default the tool browses Pi sessions. Set `provider` (one of `claude`,
|
|
48
|
+
`codex`, `crush`, `gemini`, `goose`, `opencode`, `pi`) with a `contextId` to
|
|
49
|
+
browse another tool's sessions; results render the same way as Pi sessions.
|
|
50
|
+
Non-pi providers have no current session, so they require an explicit
|
|
51
|
+
`contextId`.
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
goosedump({ action: "list", provider: "claude" })
|
|
55
|
+
goosedump({ action: "view", provider: "claude", contextId: "abc123" })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### Raw native output
|
|
59
|
+
|
|
60
|
+
By default every action renders through the normal structured view. Set
|
|
61
|
+
`outputFormat` to emit the raw native session instead — one of `claude`,
|
|
62
|
+
`codex`, `crush`, `gemini`, `goose`, `opencode`, `pi`, or `json`. JSONL providers
|
|
63
|
+
(`claude`, `codex`, `pi`, `gemini`) emit the raw session file; SQLite providers
|
|
64
|
+
(`crush`, `goose`, `opencode`) emit a row-oriented JSON projection of their
|
|
65
|
+
tables; `json` emits goosedump's structured JSON. This is round-trippable and
|
|
66
|
+
applies to `view`, `expand`, `grep`, and `search`.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
goosedump({ action: "view", outputFormat: "goose" })
|
|
70
|
+
goosedump({ action: "grep", pattern: "*rand*", outputFormat: "claude" })
|
|
71
|
+
```
|
|
72
|
+
|
|
45
73
|
### Command: `/goosedump`
|
|
46
74
|
|
|
47
75
|
Opens an interactive session browser.
|
package/index.ts
CHANGED
|
@@ -29,7 +29,7 @@ import { Type } from '@sinclair/typebox';
|
|
|
29
29
|
|
|
30
30
|
const require = createRequire(import.meta.url);
|
|
31
31
|
|
|
32
|
-
const GOOSEDUMP_VERSION = [0,
|
|
32
|
+
const GOOSEDUMP_VERSION = [0, 6, 2] as const;
|
|
33
33
|
|
|
34
34
|
interface GoosedumpListing {
|
|
35
35
|
id: string;
|
|
@@ -234,7 +234,10 @@ function textResult(text: string): AgentToolResult<void> {
|
|
|
234
234
|
return { content: [{ type: 'text', text }], details: undefined };
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
function missingContextId(action: string): AgentToolResult<void> {
|
|
237
|
+
function missingContextId(action: string, provider = 'pi'): AgentToolResult<void> {
|
|
238
|
+
if (provider !== 'pi') {
|
|
239
|
+
return textResult(`contextId is required for ${action} when provider is "${provider}"`);
|
|
240
|
+
}
|
|
238
241
|
return textResult(`contextId is required for ${action} when no current session is available`);
|
|
239
242
|
}
|
|
240
243
|
|
|
@@ -244,14 +247,16 @@ function runGoosedump(args: string[]): string {
|
|
|
244
247
|
});
|
|
245
248
|
}
|
|
246
249
|
|
|
247
|
-
function goosedumpList(): GoosedumpListing[] {
|
|
248
|
-
const
|
|
250
|
+
function goosedumpList(provider = 'pi'): GoosedumpListing[] {
|
|
251
|
+
const prefix = `${provider}:`;
|
|
252
|
+
const ids = runGoosedump(['list', `${prefix}*`])
|
|
249
253
|
.split('\n')
|
|
250
254
|
.map((line) => line.trim())
|
|
251
|
-
.filter((line) => line.startsWith(
|
|
252
|
-
.map((line) => line.slice(
|
|
255
|
+
.filter((line) => line.startsWith(prefix))
|
|
256
|
+
.map((line) => line.slice(prefix.length));
|
|
253
257
|
|
|
254
|
-
|
|
258
|
+
// Only pi sessions live in the local sessions tree we can map back to files.
|
|
259
|
+
const paths = provider === 'pi' ? indexPiSessionPaths() : new Map<string, string>();
|
|
255
260
|
return ids.map((id) => ({ id, path: paths.get(id) ?? null }));
|
|
256
261
|
}
|
|
257
262
|
|
|
@@ -289,12 +294,12 @@ function indexPiSessionPaths(): Map<string, string> {
|
|
|
289
294
|
}
|
|
290
295
|
|
|
291
296
|
function goosedumpSearch(
|
|
292
|
-
|
|
297
|
+
target: string,
|
|
293
298
|
query: string,
|
|
294
299
|
options: { scope?: string; page?: number } = {},
|
|
295
300
|
): GoosedumpSearchResult {
|
|
296
301
|
const scope = options.scope ?? 'lineage';
|
|
297
|
-
const args = ['search',
|
|
302
|
+
const args = ['search', target, query];
|
|
298
303
|
if (options.page) args.push('--page', String(options.page));
|
|
299
304
|
args.push('--scope', scope);
|
|
300
305
|
|
|
@@ -304,23 +309,23 @@ function goosedumpSearch(
|
|
|
304
309
|
}
|
|
305
310
|
|
|
306
311
|
function goosedumpGrep(
|
|
307
|
-
|
|
312
|
+
target: string,
|
|
308
313
|
pattern: string,
|
|
309
314
|
options: { scope?: string } = {},
|
|
310
315
|
): GoosedumpMessage[] {
|
|
311
316
|
const scope = options.scope ?? 'lineage';
|
|
312
|
-
const json = JSON.parse(runGoosedump(['grep',
|
|
317
|
+
const json = JSON.parse(runGoosedump(['grep', target, pattern, '--scope', scope])) as {
|
|
313
318
|
hits?: HitJson[];
|
|
314
319
|
};
|
|
315
320
|
return (json.hits ?? []).map(hitMessage);
|
|
316
321
|
}
|
|
317
322
|
|
|
318
323
|
function goosedumpShow(
|
|
319
|
-
|
|
324
|
+
target: string,
|
|
320
325
|
options: { scope?: string; ids?: string[] } = {},
|
|
321
326
|
): GoosedumpMessage[] {
|
|
322
327
|
const scope = options.scope ?? 'lineage';
|
|
323
|
-
const args = ['show',
|
|
328
|
+
const args = ['show', target];
|
|
324
329
|
if (options.ids && options.ids.length > 0) args.push('--ids', options.ids.join(','));
|
|
325
330
|
args.push('--scope', scope);
|
|
326
331
|
|
|
@@ -328,6 +333,35 @@ function goosedumpShow(
|
|
|
328
333
|
return (json.messages ?? []).map(showMessage);
|
|
329
334
|
}
|
|
330
335
|
|
|
336
|
+
// goosedump 0.6.2+ accepts --output-format on show/grep/search to emit a context
|
|
337
|
+
// in a provider's native, round-trippable format. That output is raw native text
|
|
338
|
+
// (not the ShowJson/HitJson shapes), so this bypasses the JSON-parsing wrappers
|
|
339
|
+
// and returns it verbatim. Only used when the caller explicitly sets outputFormat.
|
|
340
|
+
function goosedumpConvert(
|
|
341
|
+
subcommand: 'show' | 'grep' | 'search',
|
|
342
|
+
target: string,
|
|
343
|
+
outputFormat: string,
|
|
344
|
+
options: { positionals?: string[]; scope?: string; ids?: string[]; page?: number } = {},
|
|
345
|
+
): string {
|
|
346
|
+
const args = [subcommand, target, ...(options.positionals ?? [])];
|
|
347
|
+
if (options.ids && options.ids.length > 0) args.push('--ids', options.ids.join(','));
|
|
348
|
+
if (options.page) args.push('--page', String(options.page));
|
|
349
|
+
args.push('--scope', options.scope ?? 'lineage', '--output-format', outputFormat);
|
|
350
|
+
return runGoosedump(args);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Resolve a <provider>:<contextId> target. contextId falls back to the current
|
|
354
|
+
// session only for pi; other providers have no current session, so they require
|
|
355
|
+
// an explicit contextId.
|
|
356
|
+
function resolveTarget(
|
|
357
|
+
ctx: ExtensionContext,
|
|
358
|
+
provider: string,
|
|
359
|
+
contextId: string | undefined,
|
|
360
|
+
): string | null {
|
|
361
|
+
const id = contextId ?? (provider === 'pi' ? currentSessionContextId(ctx) : null);
|
|
362
|
+
return id ? `${provider}:${id}` : null;
|
|
363
|
+
}
|
|
364
|
+
|
|
331
365
|
function capList(items: string[], limit: number): string {
|
|
332
366
|
const shown = items.slice(0, limit).join(', ');
|
|
333
367
|
return items.length > limit ? `${shown} (+${items.length - limit} more)` : shown;
|
|
@@ -859,14 +893,16 @@ export function createGoosedumpIntegration(
|
|
|
859
893
|
name: 'goosedump',
|
|
860
894
|
label: 'goosedump',
|
|
861
895
|
description:
|
|
862
|
-
|
|
896
|
+
"Browse coding agent session history across providers. List sessions, or view/search/grep the current Pi session or a named session. Set provider to browse another tool's sessions (claude, codex, gemini, ...); set outputFormat for raw native output. Supports ranked search, glob filtering, compact overview, and result expansion. Default scope is active lineage.",
|
|
863
897
|
promptSnippet:
|
|
864
|
-
'goosedump({ action: "search", query, contextId?, scope?, page? }) - search session history; omit contextId for current session',
|
|
898
|
+
'goosedump({ action: "search", query, contextId?, provider?, scope?, page? }) - search session history; omit contextId for current Pi session',
|
|
865
899
|
promptGuidelines: [
|
|
866
900
|
'When researching past conversation history, use goosedump to find relevant context.',
|
|
867
901
|
'Start with compact ranked search (compact: true) for quick overview, then expand interesting entries.',
|
|
868
902
|
'Default scope is "lineage" (current branch); use scope: "all" to include all entries in the session.',
|
|
869
903
|
'Omit contextId to search, expand, or view the current Pi session; use list first for other sessions.',
|
|
904
|
+
'Set provider (e.g. "claude", "gemini") with a contextId to browse another tool\'s sessions; results render the same way as Pi sessions.',
|
|
905
|
+
'Set outputFormat (e.g. "claude", "json") only when you explicitly need the raw native session instead of the rendered view.',
|
|
870
906
|
],
|
|
871
907
|
parameters: Type.Object({
|
|
872
908
|
action: Type.Union(
|
|
@@ -885,7 +921,7 @@ export function createGoosedumpIntegration(
|
|
|
885
921
|
contextId: Type.Optional(
|
|
886
922
|
Type.String({
|
|
887
923
|
description:
|
|
888
|
-
'Context/session ID
|
|
924
|
+
'Context/session ID. Defaults to the current Pi session for search, grep, expand, view; required when provider is not pi.',
|
|
889
925
|
}),
|
|
890
926
|
),
|
|
891
927
|
query: Type.Optional(
|
|
@@ -918,6 +954,42 @@ export function createGoosedumpIntegration(
|
|
|
918
954
|
description: 'Page number for ranked results',
|
|
919
955
|
}),
|
|
920
956
|
),
|
|
957
|
+
provider: Type.Optional(
|
|
958
|
+
Type.Union(
|
|
959
|
+
[
|
|
960
|
+
Type.Literal('pi'),
|
|
961
|
+
Type.Literal('claude'),
|
|
962
|
+
Type.Literal('codex'),
|
|
963
|
+
Type.Literal('crush'),
|
|
964
|
+
Type.Literal('gemini'),
|
|
965
|
+
Type.Literal('goose'),
|
|
966
|
+
Type.Literal('opencode'),
|
|
967
|
+
],
|
|
968
|
+
{
|
|
969
|
+
default: 'pi',
|
|
970
|
+
description:
|
|
971
|
+
'Source provider whose sessions to browse (list, view, search, grep, expand). Defaults to pi. Non-pi providers have no current session, so they require an explicit contextId. Results render through the normal structured view regardless of provider.',
|
|
972
|
+
},
|
|
973
|
+
),
|
|
974
|
+
),
|
|
975
|
+
outputFormat: Type.Optional(
|
|
976
|
+
Type.Union(
|
|
977
|
+
[
|
|
978
|
+
Type.Literal('json'),
|
|
979
|
+
Type.Literal('claude'),
|
|
980
|
+
Type.Literal('codex'),
|
|
981
|
+
Type.Literal('crush'),
|
|
982
|
+
Type.Literal('gemini'),
|
|
983
|
+
Type.Literal('goose'),
|
|
984
|
+
Type.Literal('opencode'),
|
|
985
|
+
Type.Literal('pi'),
|
|
986
|
+
],
|
|
987
|
+
{
|
|
988
|
+
description:
|
|
989
|
+
"Explicitly emit raw native output instead of the normal rendered view (view, expand, grep, search). JSONL providers (claude, codex, pi, gemini) emit raw session JSONL; SQLite providers (crush, goose, opencode) emit a JSON table projection; json emits goosedump's structured JSON. Omit to render normally.",
|
|
990
|
+
},
|
|
991
|
+
),
|
|
992
|
+
),
|
|
921
993
|
}),
|
|
922
994
|
async execute(_id, params, _signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
|
|
923
995
|
if (!goosedumpAvailable) {
|
|
@@ -929,7 +1001,7 @@ export function createGoosedumpIntegration(
|
|
|
929
1001
|
try {
|
|
930
1002
|
switch (params.action) {
|
|
931
1003
|
case 'list': {
|
|
932
|
-
const listings = goosedumpList();
|
|
1004
|
+
const listings = goosedumpList(params.provider ?? 'pi');
|
|
933
1005
|
if (listings.length === 0) {
|
|
934
1006
|
return textResult('No sessions found.');
|
|
935
1007
|
}
|
|
@@ -943,12 +1015,23 @@ export function createGoosedumpIntegration(
|
|
|
943
1015
|
}
|
|
944
1016
|
|
|
945
1017
|
case 'search': {
|
|
946
|
-
const
|
|
947
|
-
|
|
1018
|
+
const provider = params.provider ?? 'pi';
|
|
1019
|
+
const target = resolveTarget(ctx, provider, params.contextId);
|
|
1020
|
+
if (!target) return missingContextId('search', provider);
|
|
948
1021
|
|
|
949
1022
|
if (!params.query) return textResult('query is required for search');
|
|
950
1023
|
|
|
951
|
-
|
|
1024
|
+
if (params.outputFormat) {
|
|
1025
|
+
return textResult(
|
|
1026
|
+
goosedumpConvert('search', target, params.outputFormat, {
|
|
1027
|
+
positionals: [params.query],
|
|
1028
|
+
scope: params.scope,
|
|
1029
|
+
page: params.page ?? 1,
|
|
1030
|
+
}),
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
const result = goosedumpSearch(target, params.query, {
|
|
952
1035
|
scope: params.scope ?? 'lineage',
|
|
953
1036
|
page: params.page ?? 1,
|
|
954
1037
|
});
|
|
@@ -967,12 +1050,22 @@ export function createGoosedumpIntegration(
|
|
|
967
1050
|
}
|
|
968
1051
|
|
|
969
1052
|
case 'grep': {
|
|
970
|
-
const
|
|
971
|
-
|
|
1053
|
+
const provider = params.provider ?? 'pi';
|
|
1054
|
+
const target = resolveTarget(ctx, provider, params.contextId);
|
|
1055
|
+
if (!target) return missingContextId('grep', provider);
|
|
972
1056
|
|
|
973
1057
|
if (!params.pattern) return textResult('pattern is required for grep');
|
|
974
1058
|
|
|
975
|
-
|
|
1059
|
+
if (params.outputFormat) {
|
|
1060
|
+
return textResult(
|
|
1061
|
+
goosedumpConvert('grep', target, params.outputFormat, {
|
|
1062
|
+
positionals: [params.pattern],
|
|
1063
|
+
scope: params.scope,
|
|
1064
|
+
}),
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const messages = goosedumpGrep(target, params.pattern, {
|
|
976
1069
|
scope: params.scope ?? 'lineage',
|
|
977
1070
|
});
|
|
978
1071
|
|
|
@@ -989,14 +1082,24 @@ export function createGoosedumpIntegration(
|
|
|
989
1082
|
}
|
|
990
1083
|
|
|
991
1084
|
case 'expand': {
|
|
992
|
-
const
|
|
993
|
-
|
|
1085
|
+
const provider = params.provider ?? 'pi';
|
|
1086
|
+
const target = resolveTarget(ctx, provider, params.contextId);
|
|
1087
|
+
if (!target) return missingContextId('expand', provider);
|
|
994
1088
|
|
|
995
1089
|
if (!params.ids || params.ids.length === 0) {
|
|
996
1090
|
return textResult('ids is required for expand (array of entry IDs)');
|
|
997
1091
|
}
|
|
998
1092
|
|
|
999
|
-
|
|
1093
|
+
if (params.outputFormat) {
|
|
1094
|
+
return textResult(
|
|
1095
|
+
goosedumpConvert('show', target, params.outputFormat, {
|
|
1096
|
+
ids: params.ids,
|
|
1097
|
+
scope: params.scope,
|
|
1098
|
+
}),
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
const messages = goosedumpShow(target, {
|
|
1000
1103
|
scope: params.scope ?? 'lineage',
|
|
1001
1104
|
ids: params.ids,
|
|
1002
1105
|
});
|
|
@@ -1009,13 +1112,24 @@ export function createGoosedumpIntegration(
|
|
|
1009
1112
|
}
|
|
1010
1113
|
|
|
1011
1114
|
case 'view': {
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1115
|
+
const provider = params.provider ?? 'pi';
|
|
1116
|
+
const target = resolveTarget(ctx, provider, params.contextId);
|
|
1117
|
+
if (!target) return missingContextId('view', provider);
|
|
1118
|
+
|
|
1119
|
+
if (params.outputFormat) {
|
|
1120
|
+
return textResult(
|
|
1121
|
+
goosedumpConvert('show', target, params.outputFormat, {
|
|
1122
|
+
scope: params.scope,
|
|
1123
|
+
}),
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1014
1126
|
|
|
1015
|
-
const messages = goosedumpShow(
|
|
1127
|
+
const messages = goosedumpShow(target, { scope: params.scope ?? 'lineage' });
|
|
1016
1128
|
|
|
1017
1129
|
if (messages.length === 0) {
|
|
1018
|
-
return textResult(
|
|
1130
|
+
return textResult(
|
|
1131
|
+
`Session "${target.slice(provider.length + 1)}" has no messages.`,
|
|
1132
|
+
);
|
|
1019
1133
|
}
|
|
1020
1134
|
|
|
1021
1135
|
return textResult(formatMessagesFull(messages));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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.6.2",
|
|
33
33
|
"@sinclair/typebox": "^0.34.49"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|