pi-goosedump 0.6.3 → 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 +74 -64
- 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 {
|
|
@@ -1003,7 +1014,8 @@ export function createGoosedumpIntegration(
|
|
|
1003
1014
|
|
|
1004
1015
|
const lines = ['Available sessions:', ''];
|
|
1005
1016
|
for (const listing of listings) {
|
|
1006
|
-
|
|
1017
|
+
const suffix = listing.label ? `${listing.cwd} ${listing.label}` : listing.cwd;
|
|
1018
|
+
lines.push(` ${listing.id} ${suffix}`);
|
|
1007
1019
|
}
|
|
1008
1020
|
|
|
1009
1021
|
return textResult(lines.join('\n'));
|
|
@@ -1122,9 +1134,7 @@ export function createGoosedumpIntegration(
|
|
|
1122
1134
|
const messages = goosedumpShow(target, { scope: params.scope ?? 'lineage' });
|
|
1123
1135
|
|
|
1124
1136
|
if (messages.length === 0) {
|
|
1125
|
-
return textResult(
|
|
1126
|
-
`Session "${target.slice(provider.length + 1)}" has no messages.`,
|
|
1127
|
-
);
|
|
1137
|
+
return textResult(`Session "${target}" has no messages.`);
|
|
1128
1138
|
}
|
|
1129
1139
|
|
|
1130
1140
|
return textResult(formatMessagesFull(messages));
|
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": {
|