pi-goosedump 0.6.2 → 0.6.4
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 +8 -7
- package/index.ts +91 -78
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,7 +21,8 @@ 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 Pi session by default or
|
|
24
|
+
The agent can browse the current Pi session by default, or any session by its
|
|
25
|
+
8-hex `contextId` (the id shown by `list`):
|
|
25
26
|
|
|
26
27
|
| Action | Description |
|
|
27
28
|
| -------- | ---------------------------------------- |
|
|
@@ -36,7 +37,7 @@ Examples:
|
|
|
36
37
|
```
|
|
37
38
|
goosedump({ action: "list" })
|
|
38
39
|
goosedump({ action: "search", query: "bug fix" })
|
|
39
|
-
goosedump({ action: "search", contextId: "
|
|
40
|
+
goosedump({ action: "search", contextId: "0000001f", query: "bug fix" })
|
|
40
41
|
goosedump({ action: "grep", pattern: "*rand*" })
|
|
41
42
|
goosedump({ action: "expand", ids: ["entry-a", "entry-b"] })
|
|
42
43
|
goosedump({ action: "view" })
|
|
@@ -52,7 +53,7 @@ Non-pi providers have no current session, so they require an explicit
|
|
|
52
53
|
|
|
53
54
|
```
|
|
54
55
|
goosedump({ action: "list", provider: "claude" })
|
|
55
|
-
goosedump({ action: "view", provider: "claude", contextId: "
|
|
56
|
+
goosedump({ action: "view", provider: "claude", contextId: "0000001f" })
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
#### Raw native output
|
|
@@ -76,10 +77,10 @@ Opens an interactive session browser.
|
|
|
76
77
|
|
|
77
78
|
### Compaction
|
|
78
79
|
|
|
79
|
-
pi-goosedump hooks Pi's `/compact` and auto-compaction flow. It
|
|
80
|
-
`goosedump compact
|
|
81
|
-
`--until`, and `--scope`) so the generated summary
|
|
82
|
-
about to replace.
|
|
80
|
+
pi-goosedump hooks Pi's `/compact` and auto-compaction flow. It resolves the
|
|
81
|
+
current session to its goosedump id and runs `goosedump compact <id>` with Pi's
|
|
82
|
+
compaction range (`--from`, `--until`, and `--scope`) so the generated summary
|
|
83
|
+
matches the entries Pi is about to replace.
|
|
83
84
|
|
|
84
85
|
## License
|
|
85
86
|
|
package/index.ts
CHANGED
|
@@ -13,10 +13,10 @@ import type {
|
|
|
13
13
|
import { defineTool } from '@earendil-works/pi-coding-agent';
|
|
14
14
|
|
|
15
15
|
import { execFileSync } from 'node:child_process';
|
|
16
|
-
import {
|
|
16
|
+
import { existsSync } from 'node:fs';
|
|
17
17
|
import { createRequire } from 'node:module';
|
|
18
18
|
import { homedir } from 'node:os';
|
|
19
|
-
import { basename, extname, join } from 'node:path';
|
|
19
|
+
import { basename, extname, isAbsolute, join, relative } from 'node:path';
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
22
|
Key,
|
|
@@ -29,10 +29,27 @@ 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, 7, 4] as const;
|
|
33
|
+
|
|
34
|
+
// One element of goosedump 0.7's `list` JSON output.
|
|
35
|
+
interface ListEntryJson {
|
|
36
|
+
id: string;
|
|
37
|
+
parent: string | null;
|
|
38
|
+
provider: string;
|
|
39
|
+
native_id: string;
|
|
40
|
+
cwd: string;
|
|
41
|
+
count: number;
|
|
42
|
+
label: string | null;
|
|
43
|
+
from: string | null;
|
|
44
|
+
until: string | null;
|
|
45
|
+
}
|
|
33
46
|
|
|
34
47
|
interface GoosedumpListing {
|
|
35
48
|
id: string;
|
|
49
|
+
provider: string;
|
|
50
|
+
nativeId: string;
|
|
51
|
+
cwd: string;
|
|
52
|
+
label: string | null;
|
|
36
53
|
path: string | null;
|
|
37
54
|
}
|
|
38
55
|
|
|
@@ -248,16 +265,18 @@ function runGoosedump(args: string[]): string {
|
|
|
248
265
|
}
|
|
249
266
|
|
|
250
267
|
function goosedumpList(provider = 'pi'): GoosedumpListing[] {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
.
|
|
254
|
-
.map((
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
268
|
+
const entries = JSON.parse(runGoosedump(['list'])) as ListEntryJson[];
|
|
269
|
+
return entries
|
|
270
|
+
.filter((entry) => entry.provider === provider)
|
|
271
|
+
.map((entry) => ({
|
|
272
|
+
id: entry.id,
|
|
273
|
+
provider: entry.provider,
|
|
274
|
+
nativeId: entry.native_id,
|
|
275
|
+
cwd: entry.cwd,
|
|
276
|
+
label: entry.label ?? null,
|
|
277
|
+
// Only pi sessions map back to a local file we can resume.
|
|
278
|
+
path: provider === 'pi' ? piSessionFilePath(entry.native_id) : null,
|
|
279
|
+
}));
|
|
261
280
|
}
|
|
262
281
|
|
|
263
282
|
function piSessionsDir(): string {
|
|
@@ -267,30 +286,11 @@ function piSessionsDir(): string {
|
|
|
267
286
|
return join(agentDir, 'sessions');
|
|
268
287
|
}
|
|
269
288
|
|
|
270
|
-
// goosedump
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const walk = (dir: string): void => {
|
|
276
|
-
let entries: Dirent[];
|
|
277
|
-
try {
|
|
278
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
279
|
-
} catch {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
for (const entry of entries) {
|
|
283
|
-
const full = join(dir, entry.name);
|
|
284
|
-
if (entry.isDirectory()) {
|
|
285
|
-
walk(full);
|
|
286
|
-
} else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
|
|
287
|
-
const id = sessionFileContextId(full);
|
|
288
|
-
if (id && !paths.has(id)) paths.set(id, full);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
walk(piSessionsDir());
|
|
293
|
-
return paths;
|
|
289
|
+
// goosedump derives a pi native id from the session file path relative to the
|
|
290
|
+
// sessions base (without extension), so reconstruct the file path the same way.
|
|
291
|
+
function piSessionFilePath(nativeId: string): string | null {
|
|
292
|
+
const path = join(piSessionsDir(), `${nativeId}.jsonl`);
|
|
293
|
+
return existsSync(path) ? path : null;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
function goosedumpSearch(
|
|
@@ -326,17 +326,17 @@ function goosedumpShow(
|
|
|
326
326
|
): GoosedumpMessage[] {
|
|
327
327
|
const scope = options.scope ?? 'lineage';
|
|
328
328
|
const args = ['show', target];
|
|
329
|
-
|
|
329
|
+
for (const id of options.ids ?? []) args.push('-m', id);
|
|
330
330
|
args.push('--scope', scope);
|
|
331
331
|
|
|
332
332
|
const json = JSON.parse(runGoosedump(args)) as ShowJson;
|
|
333
333
|
return (json.messages ?? []).map(showMessage);
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
-
// goosedump
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
//
|
|
336
|
+
// goosedump accepts --output-format on show/grep/search to emit a context in a
|
|
337
|
+
// provider's native, round-trippable format. That output is raw native text (not
|
|
338
|
+
// the ShowJson/HitJson shapes), so this bypasses the JSON-parsing wrappers and
|
|
339
|
+
// returns it verbatim. Only used when the caller explicitly sets outputFormat.
|
|
340
340
|
function goosedumpConvert(
|
|
341
341
|
subcommand: 'show' | 'grep' | 'search',
|
|
342
342
|
target: string,
|
|
@@ -344,22 +344,23 @@ function goosedumpConvert(
|
|
|
344
344
|
options: { positionals?: string[]; scope?: string; ids?: string[]; page?: number } = {},
|
|
345
345
|
): string {
|
|
346
346
|
const args = [subcommand, target, ...(options.positionals ?? [])];
|
|
347
|
-
|
|
347
|
+
for (const id of options.ids ?? []) args.push('-m', id);
|
|
348
348
|
if (options.page) args.push('--page', String(options.page));
|
|
349
349
|
args.push('--scope', options.scope ?? 'lineage', '--output-format', outputFormat);
|
|
350
350
|
return runGoosedump(args);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
// Resolve a
|
|
354
|
-
//
|
|
355
|
-
//
|
|
353
|
+
// Resolve a goosedump target id. In 0.7 every context is addressed by its 8-hex
|
|
354
|
+
// index id, so an explicit contextId is already the target. It falls back to the
|
|
355
|
+
// current session only for pi; other providers have no current session, so they
|
|
356
|
+
// require an explicit contextId.
|
|
356
357
|
function resolveTarget(
|
|
357
358
|
ctx: ExtensionContext,
|
|
358
359
|
provider: string,
|
|
359
360
|
contextId: string | undefined,
|
|
360
361
|
): string | null {
|
|
361
|
-
|
|
362
|
-
return
|
|
362
|
+
if (contextId) return contextId;
|
|
363
|
+
return provider === 'pi' ? currentSessionContextId(ctx) : null;
|
|
363
364
|
}
|
|
364
365
|
|
|
365
366
|
function capList(items: string[], limit: number): string {
|
|
@@ -391,7 +392,7 @@ function renderCompactSummary(c: CompactJson): string {
|
|
|
391
392
|
}
|
|
392
393
|
|
|
393
394
|
function goosedumpCompact(
|
|
394
|
-
|
|
395
|
+
target: string,
|
|
395
396
|
options: {
|
|
396
397
|
scope?: string;
|
|
397
398
|
from?: string;
|
|
@@ -400,10 +401,10 @@ function goosedumpCompact(
|
|
|
400
401
|
},
|
|
401
402
|
): string {
|
|
402
403
|
const scope = options.scope ?? 'lineage';
|
|
403
|
-
const args = ['compact',
|
|
404
|
+
const args = ['compact', target, '--scope', scope];
|
|
404
405
|
|
|
405
406
|
if (options.ids && options.ids.length > 0) {
|
|
406
|
-
|
|
407
|
+
for (const id of options.ids) args.push('-m', id);
|
|
407
408
|
} else {
|
|
408
409
|
if (options.from) args.push('--from', options.from);
|
|
409
410
|
if (options.until) args.push('--until', options.until);
|
|
@@ -445,18 +446,28 @@ export default function (pi: ExtensionAPI) {
|
|
|
445
446
|
createGoosedumpIntegration().register(pi);
|
|
446
447
|
}
|
|
447
448
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
449
|
+
// Compute the pi native id goosedump assigns to a session file: its path relative
|
|
450
|
+
// to the sessions base without the .jsonl extension, or the bare stem when the
|
|
451
|
+
// file lives outside that base (mirroring goosedump's own fallback).
|
|
452
|
+
function sessionFileNativeId(sessionFile: string): string {
|
|
453
|
+
const rel = relative(piSessionsDir(), sessionFile);
|
|
454
|
+
if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {
|
|
455
|
+
const name = basename(sessionFile);
|
|
456
|
+
const ext = extname(name);
|
|
457
|
+
return ext ? name.slice(0, -ext.length) : name;
|
|
458
|
+
}
|
|
459
|
+
const ext = extname(rel);
|
|
460
|
+
return ext ? rel.slice(0, -ext.length) : rel;
|
|
456
461
|
}
|
|
457
462
|
|
|
463
|
+
// Resolve the current pi session to its goosedump 8-hex context id by matching the
|
|
464
|
+
// session file's native id against goosedump's index (via `list`).
|
|
458
465
|
function currentSessionContextId(ctx: ExtensionContext): string | null {
|
|
459
|
-
|
|
466
|
+
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
467
|
+
if (!sessionFile) return null;
|
|
468
|
+
const nativeId = sessionFileNativeId(sessionFile);
|
|
469
|
+
const listing = goosedumpList('pi').find((entry) => entry.nativeId === nativeId);
|
|
470
|
+
return listing ? listing.id : null;
|
|
460
471
|
}
|
|
461
472
|
|
|
462
473
|
function isCompactionEntry(entry: SessionEntry): entry is CompactionEntry {
|
|
@@ -897,18 +908,21 @@ export function createGoosedumpIntegration(
|
|
|
897
908
|
'Set outputFormat (e.g. "claude", "json") only when you explicitly need the raw native session instead of the rendered view.',
|
|
898
909
|
],
|
|
899
910
|
parameters: Type.Object({
|
|
900
|
-
action: Type.
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
911
|
+
action: Type.Optional(
|
|
912
|
+
Type.Union(
|
|
913
|
+
[
|
|
914
|
+
Type.Literal('list'),
|
|
915
|
+
Type.Literal('search'),
|
|
916
|
+
Type.Literal('grep'),
|
|
917
|
+
Type.Literal('expand'),
|
|
918
|
+
Type.Literal('view'),
|
|
919
|
+
],
|
|
920
|
+
{
|
|
921
|
+
default: 'list',
|
|
922
|
+
description:
|
|
923
|
+
'Operation: list sessions, search within a session, grep by glob pattern, expand entries, or view full session. Defaults to list.',
|
|
924
|
+
},
|
|
925
|
+
),
|
|
912
926
|
),
|
|
913
927
|
contextId: Type.Optional(
|
|
914
928
|
Type.String({
|
|
@@ -991,7 +1005,7 @@ export function createGoosedumpIntegration(
|
|
|
991
1005
|
}
|
|
992
1006
|
|
|
993
1007
|
try {
|
|
994
|
-
switch (params.action) {
|
|
1008
|
+
switch (params.action ?? 'list') {
|
|
995
1009
|
case 'list': {
|
|
996
1010
|
const listings = goosedumpList(params.provider ?? 'pi');
|
|
997
1011
|
if (listings.length === 0) {
|
|
@@ -1000,7 +1014,8 @@ export function createGoosedumpIntegration(
|
|
|
1000
1014
|
|
|
1001
1015
|
const lines = ['Available sessions:', ''];
|
|
1002
1016
|
for (const listing of listings) {
|
|
1003
|
-
|
|
1017
|
+
const suffix = listing.label ? `${listing.cwd} ${listing.label}` : listing.cwd;
|
|
1018
|
+
lines.push(` ${listing.id} ${suffix}`);
|
|
1004
1019
|
}
|
|
1005
1020
|
|
|
1006
1021
|
return textResult(lines.join('\n'));
|
|
@@ -1119,16 +1134,14 @@ export function createGoosedumpIntegration(
|
|
|
1119
1134
|
const messages = goosedumpShow(target, { scope: params.scope ?? 'lineage' });
|
|
1120
1135
|
|
|
1121
1136
|
if (messages.length === 0) {
|
|
1122
|
-
return textResult(
|
|
1123
|
-
`Session "${target.slice(provider.length + 1)}" has no messages.`,
|
|
1124
|
-
);
|
|
1137
|
+
return textResult(`Session "${target}" has no messages.`);
|
|
1125
1138
|
}
|
|
1126
1139
|
|
|
1127
1140
|
return textResult(formatMessagesFull(messages));
|
|
1128
1141
|
}
|
|
1129
1142
|
|
|
1130
1143
|
default:
|
|
1131
|
-
return textResult(`Unknown action: ${params.action}`);
|
|
1144
|
+
return textResult(`Unknown action: ${params.action ?? 'list'}`);
|
|
1132
1145
|
}
|
|
1133
1146
|
} catch (err) {
|
|
1134
1147
|
const message = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
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.7.4",
|
|
33
33
|
"@sinclair/typebox": "^0.34.49"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|