pi-goosedump 0.3.5 → 0.4.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 +1 -1
- package/index.ts +170 -94
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Opens an interactive session browser.
|
|
|
47
47
|
### Compaction
|
|
48
48
|
|
|
49
49
|
pi-goosedump hooks Pi's `/compact` and auto-compaction flow. It uses
|
|
50
|
-
`goosedump compact pi
|
|
50
|
+
`goosedump compact pi:<session>` with Pi's compaction range (`--from`,
|
|
51
51
|
`--until`, and `--scope`) so the generated summary matches the entries Pi is
|
|
52
52
|
about to replace.
|
|
53
53
|
|
package/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ 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, 4, 2] as const;
|
|
27
27
|
|
|
28
28
|
interface GoosedumpListing {
|
|
29
29
|
id: string;
|
|
@@ -43,6 +43,62 @@ interface GoosedumpContextResult {
|
|
|
43
43
|
totalPages: number;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
interface ListJson {
|
|
47
|
+
sessions: { id: string }[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface ToolCallJson {
|
|
51
|
+
name: string;
|
|
52
|
+
arguments: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface ShowMessageJson {
|
|
56
|
+
entryId: string;
|
|
57
|
+
kind: 'text' | 'assistant' | 'toolResult' | 'bash';
|
|
58
|
+
role?: string;
|
|
59
|
+
text?: string;
|
|
60
|
+
thinking?: string[];
|
|
61
|
+
toolCalls?: ToolCallJson[];
|
|
62
|
+
toolName?: string;
|
|
63
|
+
content?: string;
|
|
64
|
+
isError?: boolean;
|
|
65
|
+
command?: string;
|
|
66
|
+
output?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface ShowJson {
|
|
70
|
+
messages: ShowMessageJson[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface HitJson {
|
|
74
|
+
entryId: string;
|
|
75
|
+
role: string;
|
|
76
|
+
score: number;
|
|
77
|
+
text: string;
|
|
78
|
+
files: string[];
|
|
79
|
+
terms: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface SearchJson {
|
|
83
|
+
page: number;
|
|
84
|
+
totalPages: number;
|
|
85
|
+
total: number;
|
|
86
|
+
hits: HitJson[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface GrepJson {
|
|
90
|
+
hits: HitJson[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface CompactJson {
|
|
94
|
+
goals: string[];
|
|
95
|
+
files: { modified: string[]; created: string[]; read: string[] };
|
|
96
|
+
commits: string[];
|
|
97
|
+
outstanding: string[];
|
|
98
|
+
preferences: string[];
|
|
99
|
+
brief: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
46
102
|
function resolveGoosedumpBinary(): string {
|
|
47
103
|
try {
|
|
48
104
|
return require.resolve('@jarkkojs/goosedump/bin/goosedump.js');
|
|
@@ -92,66 +148,76 @@ function hasMinimumVersion(version: string, minimum: readonly [number, number, n
|
|
|
92
148
|
return true;
|
|
93
149
|
}
|
|
94
150
|
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
151
|
+
function clip(value: string, max: number): string {
|
|
152
|
+
return value.length > max ? `${value.slice(0, max)}...` : value;
|
|
153
|
+
}
|
|
98
154
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
155
|
+
function summarizeToolArgs(args: unknown): string {
|
|
156
|
+
if (args && typeof args === 'object' && !Array.isArray(args)) {
|
|
157
|
+
const obj = args as Record<string, unknown>;
|
|
158
|
+
for (const key of ['path', 'file_path', 'filePath', 'file']) {
|
|
159
|
+
if (typeof obj[key] === 'string') return `path=${obj[key] as string}`;
|
|
160
|
+
}
|
|
161
|
+
for (const key of ['command', 'query', 'pattern', 'description']) {
|
|
162
|
+
if (typeof obj[key] === 'string') return `${key}=${clip(obj[key] as string, 160)}`;
|
|
104
163
|
}
|
|
164
|
+
const keys = Object.keys(obj);
|
|
165
|
+
return keys.length === 0 ? '' : keys.sort().join(', ');
|
|
105
166
|
}
|
|
106
|
-
|
|
107
|
-
return
|
|
167
|
+
if (typeof args === 'string') return clip(args, 160);
|
|
168
|
+
if (args == null) return '';
|
|
169
|
+
return clip(JSON.stringify(args), 160);
|
|
108
170
|
}
|
|
109
171
|
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
172
|
+
function showMessageRole(m: ShowMessageJson): string {
|
|
173
|
+
switch (m.kind) {
|
|
174
|
+
case 'assistant':
|
|
175
|
+
return 'assistant';
|
|
176
|
+
case 'toolResult':
|
|
177
|
+
return `tool/${m.toolName ?? ''}${m.isError ? ' ⚠' : ''}`;
|
|
178
|
+
case 'bash':
|
|
179
|
+
return 'bash';
|
|
180
|
+
default:
|
|
181
|
+
return m.role && m.role.length > 0 ? m.role : 'unknown';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
118
184
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
185
|
+
function showMessageContent(m: ShowMessageJson): string {
|
|
186
|
+
switch (m.kind) {
|
|
187
|
+
case 'assistant': {
|
|
188
|
+
const parts: string[] = [];
|
|
189
|
+
if (m.thinking && m.thinking.length > 0) parts.push(m.thinking.join('\n'));
|
|
190
|
+
if (m.text) parts.push(m.text);
|
|
191
|
+
if (m.toolCalls && m.toolCalls.length > 0) {
|
|
192
|
+
parts.push(
|
|
193
|
+
m.toolCalls
|
|
194
|
+
.map((tc) => {
|
|
195
|
+
const summary = summarizeToolArgs(tc.arguments);
|
|
196
|
+
return summary ? `${tc.name}(${summary})` : tc.name;
|
|
197
|
+
})
|
|
198
|
+
.join('\n'),
|
|
199
|
+
);
|
|
124
200
|
}
|
|
125
|
-
|
|
126
|
-
id: entryMatch[1],
|
|
127
|
-
role: entryMatch[2],
|
|
128
|
-
content: '',
|
|
129
|
-
};
|
|
130
|
-
contentLines = [];
|
|
131
|
-
continue;
|
|
201
|
+
return parts.join('\n');
|
|
132
202
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const scoreVal = parseFloat(content.slice(7));
|
|
140
|
-
if (!isNaN(scoreVal)) currentMsg.score = scoreVal;
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (/^\.\.\.\(\d+ lines (?:above|below)\)$/.test(content)) continue;
|
|
144
|
-
contentLines.push(content);
|
|
145
|
-
}
|
|
203
|
+
case 'toolResult':
|
|
204
|
+
return m.content ?? '';
|
|
205
|
+
case 'bash': {
|
|
206
|
+
const cmd = m.command ? `$ ${m.command}` : '';
|
|
207
|
+
if (cmd && m.output) return `${cmd}\n${m.output}`;
|
|
208
|
+
return cmd || (m.output ?? '');
|
|
146
209
|
}
|
|
210
|
+
default:
|
|
211
|
+
return m.text ?? '';
|
|
147
212
|
}
|
|
213
|
+
}
|
|
148
214
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
215
|
+
function showMessage(m: ShowMessageJson): GoosedumpMessage {
|
|
216
|
+
return { id: m.entryId, role: showMessageRole(m), content: showMessageContent(m) };
|
|
217
|
+
}
|
|
153
218
|
|
|
154
|
-
|
|
219
|
+
function hitMessage(h: HitJson): GoosedumpMessage {
|
|
220
|
+
return { id: h.entryId, role: h.role, content: h.text, score: h.score };
|
|
155
221
|
}
|
|
156
222
|
|
|
157
223
|
function formatMessagesCompact(messages: GoosedumpMessage[]): string {
|
|
@@ -172,21 +238,9 @@ function runGoosedump(args: string[]): string {
|
|
|
172
238
|
});
|
|
173
239
|
}
|
|
174
240
|
|
|
175
|
-
function parsePageInfo(output: string, fallbackPage: number): { page: number; totalPages: number } {
|
|
176
|
-
const match = output.match(/\(page\s+(\d+)\s+of\s+(\d+)\):/i);
|
|
177
|
-
if (!match) {
|
|
178
|
-
return { page: fallbackPage, totalPages: 1 };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
page: Number(match[1]),
|
|
183
|
-
totalPages: Number(match[2]),
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
241
|
function goosedumpList(): GoosedumpListing[] {
|
|
188
|
-
const
|
|
189
|
-
return
|
|
242
|
+
const json = JSON.parse(runGoosedump(['list', 'pi:*'])) as ListJson;
|
|
243
|
+
return (json.sessions ?? []).map((session) => ({ id: session.id }));
|
|
190
244
|
}
|
|
191
245
|
|
|
192
246
|
function goosedumpContext(
|
|
@@ -194,7 +248,6 @@ function goosedumpContext(
|
|
|
194
248
|
options: {
|
|
195
249
|
scope?: string;
|
|
196
250
|
grep?: string;
|
|
197
|
-
compact?: boolean;
|
|
198
251
|
ids?: string;
|
|
199
252
|
rank?: boolean;
|
|
200
253
|
page?: number;
|
|
@@ -202,33 +255,35 @@ function goosedumpContext(
|
|
|
202
255
|
): GoosedumpContextResult {
|
|
203
256
|
const scope = options.scope ?? 'lineage';
|
|
204
257
|
const hasQuery = typeof options.grep === 'string';
|
|
258
|
+
const isSearch = hasQuery && options.rank !== false;
|
|
259
|
+
const target = `pi:${contextId}`;
|
|
205
260
|
const args = hasQuery
|
|
206
|
-
?
|
|
207
|
-
? ['
|
|
208
|
-
: ['
|
|
209
|
-
: ['show',
|
|
261
|
+
? isSearch
|
|
262
|
+
? ['search', target, options.grep ?? '']
|
|
263
|
+
: ['grep', target, options.grep ?? '']
|
|
264
|
+
: ['show', target];
|
|
210
265
|
|
|
211
|
-
if (options.compact) {
|
|
212
|
-
args.push('--clip');
|
|
213
|
-
}
|
|
214
266
|
if (options.ids) {
|
|
215
267
|
args.push('--ids', options.ids);
|
|
216
268
|
}
|
|
217
|
-
if (
|
|
269
|
+
if (isSearch && options.page) {
|
|
218
270
|
args.push('--page', String(options.page));
|
|
219
271
|
}
|
|
220
272
|
args.push('--scope', scope);
|
|
221
273
|
|
|
222
274
|
const output = runGoosedump(args);
|
|
223
|
-
const messages = parseMessages(output);
|
|
224
|
-
const pageInfo = parsePageInfo(output, options.page ?? 1);
|
|
225
275
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
page
|
|
230
|
-
totalPages
|
|
231
|
-
|
|
276
|
+
if (hasQuery) {
|
|
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 };
|
|
232
287
|
}
|
|
233
288
|
|
|
234
289
|
function goosedumpExpand(
|
|
@@ -238,16 +293,38 @@ function goosedumpExpand(
|
|
|
238
293
|
): GoosedumpMessage[] {
|
|
239
294
|
if (entryIds.length === 0) return [];
|
|
240
295
|
|
|
241
|
-
const
|
|
242
|
-
'show',
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
296
|
+
const json = JSON.parse(
|
|
297
|
+
runGoosedump(['show', `pi:${contextId}`, '--ids', entryIds.join(','), '--scope', scope]),
|
|
298
|
+
) as ShowJson;
|
|
299
|
+
return (json.messages ?? []).map(showMessage);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function capList(items: string[], limit: number): string {
|
|
303
|
+
const shown = items.slice(0, limit).join(', ');
|
|
304
|
+
return items.length > limit ? `${shown} (+${items.length - limit} more)` : shown;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function bulletSection(title: string, items: string[]): string | null {
|
|
308
|
+
if (items.length === 0) return null;
|
|
309
|
+
return `[${title}]\n${items.map((item) => `- ${item}`).join('\n')}`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function renderCompactSummary(c: CompactJson): string {
|
|
313
|
+
const fileLines: string[] = [];
|
|
314
|
+
if (c.files.modified.length > 0) fileLines.push(`Modified: ${capList(c.files.modified, 10)}`);
|
|
315
|
+
if (c.files.created.length > 0) fileLines.push(`Created: ${capList(c.files.created, 10)}`);
|
|
316
|
+
if (c.files.read.length > 0) fileLines.push(`Read: ${capList(c.files.read, 10)}`);
|
|
317
|
+
|
|
318
|
+
const parts = [
|
|
319
|
+
bulletSection('Session Goal', c.goals),
|
|
320
|
+
bulletSection('Files And Changes', fileLines),
|
|
321
|
+
bulletSection('Commits', c.commits),
|
|
322
|
+
bulletSection('Outstanding Context', c.outstanding),
|
|
323
|
+
bulletSection('User Preferences', c.preferences),
|
|
324
|
+
c.brief.trim() ? c.brief : null,
|
|
325
|
+
].filter((part): part is string => part !== null);
|
|
326
|
+
|
|
327
|
+
return parts.join('\n\n---\n\n');
|
|
251
328
|
}
|
|
252
329
|
|
|
253
330
|
function goosedumpCompact(
|
|
@@ -260,7 +337,7 @@ function goosedumpCompact(
|
|
|
260
337
|
},
|
|
261
338
|
): string {
|
|
262
339
|
const scope = options.scope ?? 'lineage';
|
|
263
|
-
const args = ['compact',
|
|
340
|
+
const args = ['compact', `pi:${contextId}`, '--scope', scope];
|
|
264
341
|
|
|
265
342
|
if (options.ids && options.ids.length > 0) {
|
|
266
343
|
args.push('--ids', options.ids.join(','));
|
|
@@ -269,7 +346,8 @@ function goosedumpCompact(
|
|
|
269
346
|
if (options.until) args.push('--until', options.until);
|
|
270
347
|
}
|
|
271
348
|
|
|
272
|
-
|
|
349
|
+
const json = JSON.parse(runGoosedump(args)) as CompactJson;
|
|
350
|
+
return renderCompactSummary(json);
|
|
273
351
|
}
|
|
274
352
|
|
|
275
353
|
export interface GoosedumpIntegrationOptions {
|
|
@@ -666,7 +744,6 @@ export function createGoosedumpIntegration(
|
|
|
666
744
|
const result = goosedumpContext(contextId, {
|
|
667
745
|
scope: params.scope ?? 'lineage',
|
|
668
746
|
grep: params.query,
|
|
669
|
-
compact: params.compact ?? true,
|
|
670
747
|
rank: true,
|
|
671
748
|
page: params.page ?? 1,
|
|
672
749
|
});
|
|
@@ -748,7 +825,6 @@ export function createGoosedumpIntegration(
|
|
|
748
825
|
|
|
749
826
|
const result = goosedumpContext(contextId, {
|
|
750
827
|
scope: params.scope ?? 'lineage',
|
|
751
|
-
compact: false,
|
|
752
828
|
});
|
|
753
829
|
|
|
754
830
|
if (result.messages.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.4.2",
|
|
33
33
|
"@sinclair/typebox": "^0.34.49"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|