pi-goosedump 0.7.3 → 0.8.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 +17 -1
- package/index.ts +399 -214
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pi-goosedump
|
|
2
2
|
|
|
3
|
+
<img alt="pi-goosedump browser dialog" src="screenshot.png">
|
|
4
|
+
|
|
3
5
|
Coding agent context data browser plugin for [pi](https://pi.dev/) using
|
|
4
6
|
[`goosedump`](https://github.com/jarkkojs/goosedump).
|
|
5
7
|
|
|
@@ -73,7 +75,9 @@ goosedump({ action: "grep", pattern: "*rand*", outputFormat: "claude" })
|
|
|
73
75
|
|
|
74
76
|
### Command: `/goosedump`
|
|
75
77
|
|
|
76
|
-
Opens an interactive
|
|
78
|
+
Opens an interactive dashboard. The sessions tab lists Pi sessions from the
|
|
79
|
+
current working directory only. The settings tab edits the global and project
|
|
80
|
+
`goosedump` settings sections.
|
|
77
81
|
|
|
78
82
|
### Compaction
|
|
79
83
|
|
|
@@ -82,6 +86,18 @@ current session to its goosedump id and runs `goosedump compact <id>` with Pi's
|
|
|
82
86
|
compaction range (`--from`, `--until`, and `--scope`) so the generated summary
|
|
83
87
|
matches the entries Pi is about to replace.
|
|
84
88
|
|
|
89
|
+
Optional goosedump counters can also trigger compaction. `0` disables a counter;
|
|
90
|
+
when either counter fires, both counters reset to their configured values.
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"goosedump": {
|
|
95
|
+
"stepsBeforeCompact": 0,
|
|
96
|
+
"turnsBeforeCompact": 0
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
85
101
|
## License
|
|
86
102
|
|
|
87
103
|
`pi-goosedump` is licensed under `MIT`. See [LICENSE](LICENSE) for more
|
package/index.ts
CHANGED
|
@@ -10,16 +10,18 @@ import type {
|
|
|
10
10
|
SessionEntry,
|
|
11
11
|
} from '@earendil-works/pi-coding-agent';
|
|
12
12
|
|
|
13
|
-
import { defineTool } from '@earendil-works/pi-coding-agent';
|
|
13
|
+
import { defineTool, getSettingsListTheme } from '@earendil-works/pi-coding-agent';
|
|
14
14
|
|
|
15
15
|
import { execFileSync } from 'node:child_process';
|
|
16
|
-
import { existsSync } from 'node:fs';
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
17
|
import { createRequire } from 'node:module';
|
|
18
18
|
import { homedir } from 'node:os';
|
|
19
|
-
import { basename, extname, isAbsolute, join, relative } from 'node:path';
|
|
19
|
+
import { basename, dirname, extname, isAbsolute, join, relative } from 'node:path';
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
22
|
Key,
|
|
23
|
+
SettingsList,
|
|
24
|
+
type SettingItem,
|
|
23
25
|
matchesKey,
|
|
24
26
|
truncateToWidth,
|
|
25
27
|
visibleWidth,
|
|
@@ -29,25 +31,24 @@ import { Type } from '@sinclair/typebox';
|
|
|
29
31
|
|
|
30
32
|
const require = createRequire(import.meta.url);
|
|
31
33
|
|
|
32
|
-
const GOOSEDUMP_VERSION = [0,
|
|
34
|
+
const GOOSEDUMP_VERSION = [0, 8, 1] as const;
|
|
33
35
|
|
|
34
|
-
// One element of goosedump 0.
|
|
36
|
+
// One element of goosedump 0.8's `list` JSON output.
|
|
35
37
|
interface ListEntryJson {
|
|
36
38
|
id: string;
|
|
37
39
|
parent: string | null;
|
|
38
40
|
provider: string;
|
|
39
|
-
native_id: string;
|
|
40
41
|
cwd: string;
|
|
41
42
|
count: number;
|
|
42
43
|
label: string | null;
|
|
43
44
|
from: string | null;
|
|
44
45
|
until: string | null;
|
|
46
|
+
native_id?: string;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
interface GoosedumpListing {
|
|
48
50
|
id: string;
|
|
49
51
|
provider: string;
|
|
50
|
-
nativeId: string;
|
|
51
52
|
cwd: string;
|
|
52
53
|
parent: string | null;
|
|
53
54
|
label: string | null;
|
|
@@ -66,24 +67,26 @@ interface GoosedumpSearchResult {
|
|
|
66
67
|
page: number;
|
|
67
68
|
totalPages: number;
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
name: string;
|
|
72
|
-
arguments: unknown;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
interface ShowMessageJson {
|
|
76
|
-
entryId: string;
|
|
77
|
-
kind: 'text' | 'assistant' | 'toolResult' | 'bash';
|
|
78
|
-
role?: string;
|
|
70
|
+
interface ShowPartJson {
|
|
71
|
+
type: 'text' | 'reasoning' | 'toolCall' | 'toolResult' | 'image' | 'bash' | 'passthrough';
|
|
79
72
|
text?: string;
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
name?: string;
|
|
74
|
+
arguments?: unknown;
|
|
82
75
|
toolName?: string;
|
|
83
76
|
content?: string;
|
|
84
77
|
isError?: boolean;
|
|
78
|
+
mimeType?: string;
|
|
79
|
+
ocrText?: string;
|
|
85
80
|
command?: string;
|
|
86
81
|
output?: string;
|
|
82
|
+
kind?: string;
|
|
83
|
+
raw?: unknown;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface ShowMessageJson {
|
|
87
|
+
entryId: string;
|
|
88
|
+
role: string;
|
|
89
|
+
parts: ShowPartJson[];
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
interface ShowJson {
|
|
@@ -115,6 +118,93 @@ interface CompactJson {
|
|
|
115
118
|
brief: string;
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
interface GoosedumpSettings {
|
|
122
|
+
stepsBeforeCompact: number;
|
|
123
|
+
turnsBeforeCompact: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type SettingsScope = 'global' | 'project';
|
|
127
|
+
type GoosedumpSettingKey = keyof GoosedumpSettings;
|
|
128
|
+
|
|
129
|
+
const DEFAULT_GOOSEDUMP_SETTINGS: GoosedumpSettings = {
|
|
130
|
+
stepsBeforeCompact: 0,
|
|
131
|
+
turnsBeforeCompact: 0,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const COMPACT_SETTING_VALUES = ['0', '5', '10', '20', '50', '100'];
|
|
135
|
+
|
|
136
|
+
function piAgentDir(): string {
|
|
137
|
+
return process.env.PI_CODING_AGENT_DIR ?? join(homedir(), '.pi', 'agent');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function globalSettingsPath(): string {
|
|
141
|
+
return join(piAgentDir(), 'settings.json');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function projectSettingsPath(cwd: string): string {
|
|
145
|
+
return join(cwd, '.pi', 'settings.json');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
149
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readJsonObject(path: string): Record<string, unknown> {
|
|
153
|
+
if (!existsSync(path)) return {};
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as unknown;
|
|
157
|
+
return isRecord(parsed) ? parsed : {};
|
|
158
|
+
} catch {
|
|
159
|
+
return {};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function readGoosedumpSettingsFrom(path: string): Partial<GoosedumpSettings> {
|
|
164
|
+
const raw = readJsonObject(path).goosedump;
|
|
165
|
+
if (!isRecord(raw)) return {};
|
|
166
|
+
|
|
167
|
+
const settings: Partial<GoosedumpSettings> = {};
|
|
168
|
+
for (const key of ['stepsBeforeCompact', 'turnsBeforeCompact'] as const) {
|
|
169
|
+
const value = raw[key];
|
|
170
|
+
if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {
|
|
171
|
+
settings[key] = value;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return settings;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function resolveGoosedumpSettings(cwd: string): GoosedumpSettings {
|
|
178
|
+
return {
|
|
179
|
+
...DEFAULT_GOOSEDUMP_SETTINGS,
|
|
180
|
+
...readGoosedumpSettingsFrom(globalSettingsPath()),
|
|
181
|
+
...readGoosedumpSettingsFrom(projectSettingsPath(cwd)),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function readScopedGoosedumpSettings(cwd: string, scope: SettingsScope): GoosedumpSettings {
|
|
186
|
+
const path = scope === 'global' ? globalSettingsPath() : projectSettingsPath(cwd);
|
|
187
|
+
return { ...DEFAULT_GOOSEDUMP_SETTINGS, ...readGoosedumpSettingsFrom(path) };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function writeScopedGoosedumpSetting(
|
|
191
|
+
cwd: string,
|
|
192
|
+
scope: SettingsScope,
|
|
193
|
+
key: GoosedumpSettingKey,
|
|
194
|
+
value: number,
|
|
195
|
+
): void {
|
|
196
|
+
const path = scope === 'global' ? globalSettingsPath() : projectSettingsPath(cwd);
|
|
197
|
+
const root = readJsonObject(path);
|
|
198
|
+
const goosedump = isRecord(root.goosedump) ? root.goosedump : {};
|
|
199
|
+
root.goosedump = { ...goosedump, [key]: value };
|
|
200
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
201
|
+
writeFileSync(path, `${JSON.stringify(root, null, 2)}\n`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function formatCompactSetting(value: number): string {
|
|
205
|
+
return value === 0 ? 'disabled' : String(value);
|
|
206
|
+
}
|
|
207
|
+
|
|
118
208
|
function resolveGoosedumpBinary(): string {
|
|
119
209
|
try {
|
|
120
210
|
return require.resolve('@jarkkojs/goosedump/bin/goosedump.js');
|
|
@@ -181,51 +271,43 @@ function summarizeToolArgs(args: unknown): string {
|
|
|
181
271
|
return clip(JSON.stringify(args), 160);
|
|
182
272
|
}
|
|
183
273
|
|
|
184
|
-
function
|
|
185
|
-
switch (
|
|
186
|
-
case '
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return '
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (m.thinking && m.thinking.length > 0) parts.push(m.thinking.join('\n'));
|
|
202
|
-
if (m.text) parts.push(m.text);
|
|
203
|
-
if (m.toolCalls && m.toolCalls.length > 0) {
|
|
204
|
-
parts.push(
|
|
205
|
-
m.toolCalls
|
|
206
|
-
.map((tc) => {
|
|
207
|
-
const summary = summarizeToolArgs(tc.arguments);
|
|
208
|
-
return summary ? `${tc.name}(${summary})` : tc.name;
|
|
209
|
-
})
|
|
210
|
-
.join('\n'),
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
return parts.join('\n');
|
|
274
|
+
function showPartContent(part: ShowPartJson): string {
|
|
275
|
+
switch (part.type) {
|
|
276
|
+
case 'text':
|
|
277
|
+
case 'reasoning':
|
|
278
|
+
return part.text ?? '';
|
|
279
|
+
case 'toolCall': {
|
|
280
|
+
const summary = summarizeToolArgs(part.arguments);
|
|
281
|
+
return summary ? `${part.name ?? 'tool'}(${summary})` : (part.name ?? 'tool');
|
|
282
|
+
}
|
|
283
|
+
case 'toolResult': {
|
|
284
|
+
const name = part.toolName ?? 'tool';
|
|
285
|
+
const header = `${name}${part.isError ? ' ⚠' : ''}`;
|
|
286
|
+
return part.content ? `${header}\n${part.content}` : header;
|
|
287
|
+
}
|
|
288
|
+
case 'image': {
|
|
289
|
+
const header = `[image ${part.mimeType ?? 'unknown'}]`;
|
|
290
|
+
return part.ocrText ? `${header}\n${part.ocrText}` : header;
|
|
214
291
|
}
|
|
215
|
-
case 'toolResult':
|
|
216
|
-
return m.content ?? '';
|
|
217
292
|
case 'bash': {
|
|
218
|
-
const cmd =
|
|
219
|
-
if (cmd &&
|
|
220
|
-
return cmd || (
|
|
293
|
+
const cmd = part.command ? `$ ${part.command}` : '';
|
|
294
|
+
if (cmd && part.output) return `${cmd}\n${part.output}`;
|
|
295
|
+
return cmd || (part.output ?? '');
|
|
296
|
+
}
|
|
297
|
+
case 'passthrough': {
|
|
298
|
+
const kind = part.kind ? `passthrough:${part.kind}` : 'passthrough';
|
|
299
|
+
const raw = part.raw === undefined ? '' : JSON.stringify(part.raw);
|
|
300
|
+
return raw ? `[${kind}] ${raw}` : `[${kind}]`;
|
|
221
301
|
}
|
|
222
|
-
default:
|
|
223
|
-
return m.text ?? '';
|
|
224
302
|
}
|
|
225
303
|
}
|
|
226
304
|
|
|
305
|
+
function showMessageContent(m: ShowMessageJson): string {
|
|
306
|
+
return m.parts.map(showPartContent).filter(Boolean).join('\n');
|
|
307
|
+
}
|
|
308
|
+
|
|
227
309
|
function showMessage(m: ShowMessageJson): GoosedumpMessage {
|
|
228
|
-
return { id: m.entryId, role:
|
|
310
|
+
return { id: m.entryId, role: m.role, content: showMessageContent(m) };
|
|
229
311
|
}
|
|
230
312
|
|
|
231
313
|
function hitMessage(h: HitJson): GoosedumpMessage {
|
|
@@ -269,16 +351,18 @@ function goosedumpList(provider = 'pi'): GoosedumpListing[] {
|
|
|
269
351
|
const entries = JSON.parse(runGoosedump(['list'])) as ListEntryJson[];
|
|
270
352
|
return entries
|
|
271
353
|
.filter((entry) => entry.provider === provider)
|
|
272
|
-
.map((entry) =>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
354
|
+
.map((entry) => {
|
|
355
|
+
const nativeId = entry.native_id ?? entry.id;
|
|
356
|
+
return {
|
|
357
|
+
id: entry.id,
|
|
358
|
+
provider: entry.provider,
|
|
359
|
+
cwd: entry.cwd,
|
|
360
|
+
parent: entry.parent,
|
|
361
|
+
label: entry.label ?? null,
|
|
362
|
+
// Only pi sessions map back to a local file we can resume.
|
|
363
|
+
path: provider === 'pi' ? piSessionFilePath(nativeId) : null,
|
|
364
|
+
};
|
|
365
|
+
});
|
|
282
366
|
}
|
|
283
367
|
|
|
284
368
|
function piSessionsDir(): string {
|
|
@@ -475,7 +559,7 @@ function currentSessionContextId(ctx: ExtensionContext): string | null {
|
|
|
475
559
|
const sessionFile = ctx.sessionManager.getSessionFile();
|
|
476
560
|
if (!sessionFile) return null;
|
|
477
561
|
const nativeId = sessionFileNativeId(sessionFile);
|
|
478
|
-
const listing = goosedumpList('pi').find((entry) => entry.
|
|
562
|
+
const listing = goosedumpList('pi').find((entry) => entry.id === nativeId);
|
|
479
563
|
return listing ? listing.id : null;
|
|
480
564
|
}
|
|
481
565
|
|
|
@@ -669,61 +753,56 @@ async function runSessionBrowser(
|
|
|
669
753
|
): Promise<string | null | undefined> {
|
|
670
754
|
let resumePath: string | null | undefined;
|
|
671
755
|
|
|
672
|
-
const OVERLAY_WIDTH =
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const siblings = childrenMap.get(l.parent) ?? [];
|
|
681
|
-
siblings.push(l);
|
|
682
|
-
childrenMap.set(l.parent, siblings);
|
|
683
|
-
}
|
|
756
|
+
const OVERLAY_WIDTH = 84;
|
|
757
|
+
const LIST_VIEWPORT = 18;
|
|
758
|
+
const COMPACT_VIEWPORT = 18;
|
|
759
|
+
const location = ctx.cwd;
|
|
760
|
+
const settingValues = COMPACT_SETTING_VALUES;
|
|
761
|
+
|
|
762
|
+
function locationListings(): GoosedumpListing[] {
|
|
763
|
+
return listings.filter((listing) => listing.cwd === location);
|
|
684
764
|
}
|
|
685
765
|
|
|
686
|
-
function
|
|
687
|
-
|
|
688
|
-
roots = listings.filter((l) => l.parent === null || !listingIds.has(l.parent));
|
|
689
|
-
childrenMap = new Map<string, GoosedumpListing[]>();
|
|
690
|
-
for (const l of listings) {
|
|
691
|
-
if (l.parent !== null) {
|
|
692
|
-
const siblings = childrenMap.get(l.parent) ?? [];
|
|
693
|
-
siblings.push(l);
|
|
694
|
-
childrenMap.set(l.parent, siblings);
|
|
695
|
-
}
|
|
696
|
-
}
|
|
766
|
+
function refreshListings(): void {
|
|
767
|
+
listings.splice(0, listings.length, ...goosedumpList());
|
|
697
768
|
}
|
|
698
769
|
|
|
699
|
-
function
|
|
700
|
-
if (
|
|
770
|
+
function shortPath(path: string, maxLen: number): string {
|
|
771
|
+
if (path.length <= maxLen) return path;
|
|
701
772
|
const head = Math.floor(maxLen / 2) + 2;
|
|
702
773
|
const tail = maxLen - head - 3;
|
|
703
|
-
if (tail < 4) return
|
|
704
|
-
return
|
|
774
|
+
if (tail < 4) return `${path.slice(0, maxLen - 3)}...`;
|
|
775
|
+
return `${path.slice(0, head)}...${path.slice(path.length - tail)}`;
|
|
705
776
|
}
|
|
706
777
|
|
|
707
|
-
function
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
778
|
+
function settingLabel(scope: SettingsScope, key: GoosedumpSettingKey): string {
|
|
779
|
+
const scopeLabel = scope === 'global' ? 'Global' : 'Project';
|
|
780
|
+
const keyLabel = key === 'stepsBeforeCompact' ? 'stepsBeforeCompact' : 'turnsBeforeCompact';
|
|
781
|
+
return `${scopeLabel} ${keyLabel}`;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function buildSettingItems(): SettingItem[] {
|
|
785
|
+
const global = readScopedGoosedumpSettings(location, 'global');
|
|
786
|
+
const project = readScopedGoosedumpSettings(location, 'project');
|
|
787
|
+
const settingsByScope = { global, project } satisfies Record<SettingsScope, GoosedumpSettings>;
|
|
788
|
+
|
|
789
|
+
return (['global', 'project'] as const).flatMap((scope) =>
|
|
790
|
+
(['stepsBeforeCompact', 'turnsBeforeCompact'] as const).map((key) => ({
|
|
791
|
+
id: `${scope}:${key}`,
|
|
792
|
+
label: settingLabel(scope, key),
|
|
793
|
+
description:
|
|
794
|
+
key === 'stepsBeforeCompact'
|
|
795
|
+
? 'Tool steps before goosedump starts compaction; 0 disables this trigger.'
|
|
796
|
+
: 'LLM turns before goosedump starts compaction; 0 disables this trigger.',
|
|
797
|
+
currentValue: String(settingsByScope[scope][key]),
|
|
798
|
+
values: settingValues,
|
|
799
|
+
})),
|
|
800
|
+
);
|
|
721
801
|
}
|
|
722
802
|
|
|
723
803
|
await ctx.ui.custom<void>(
|
|
724
804
|
(tui, theme, _kb, done) => {
|
|
725
|
-
let
|
|
726
|
-
let displayList = flattenTree(roots, expandedNodes, 0);
|
|
805
|
+
let tab: 'sessions' | 'settings' = 'sessions';
|
|
727
806
|
let selectedIndex = 0;
|
|
728
807
|
let listScroll = 0;
|
|
729
808
|
let mode: 'list' | 'compact' = 'list';
|
|
@@ -731,26 +810,45 @@ async function runSessionBrowser(
|
|
|
731
810
|
let compactScroll = 0;
|
|
732
811
|
let compactTitle = '';
|
|
733
812
|
let pendingDeletionId: string | null = null;
|
|
813
|
+
let settingsList: SettingsList;
|
|
734
814
|
|
|
735
|
-
const COMPACT_VIEWPORT = 20;
|
|
736
|
-
const LIST_VIEWPORT = 20;
|
|
737
815
|
const wrapWidth = OVERLAY_WIDTH - 4;
|
|
738
816
|
|
|
739
|
-
function
|
|
740
|
-
|
|
817
|
+
function resetSelection(): void {
|
|
818
|
+
const count = locationListings().length;
|
|
819
|
+
selectedIndex = Math.min(selectedIndex, Math.max(0, count - 1));
|
|
820
|
+
listScroll = Math.min(listScroll, Math.max(0, count - LIST_VIEWPORT));
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function rebuildSettingsList(): void {
|
|
824
|
+
settingsList = new SettingsList(
|
|
825
|
+
buildSettingItems(),
|
|
826
|
+
10,
|
|
827
|
+
getSettingsListTheme(),
|
|
828
|
+
(id, newValue) => {
|
|
829
|
+
const [scope, key] = id.split(':') as [SettingsScope, GoosedumpSettingKey];
|
|
830
|
+
const parsed = Number(newValue);
|
|
831
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) return;
|
|
832
|
+
writeScopedGoosedumpSetting(location, scope, key, parsed);
|
|
833
|
+
settingsList.updateValue(id, newValue);
|
|
834
|
+
ctx.ui.notify(`${settingLabel(scope, key)} = ${formatCompactSetting(parsed)}`, 'info');
|
|
835
|
+
tui.requestRender();
|
|
836
|
+
},
|
|
837
|
+
() => {
|
|
838
|
+
tab = 'sessions';
|
|
839
|
+
tui.requestRender();
|
|
840
|
+
},
|
|
841
|
+
);
|
|
741
842
|
}
|
|
742
843
|
|
|
844
|
+
rebuildSettingsList();
|
|
845
|
+
|
|
743
846
|
function executeDeletion(): void {
|
|
744
847
|
try {
|
|
745
848
|
const id = pendingDeletionId!;
|
|
746
849
|
goosedumpDelete(id);
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
rebuildTree();
|
|
750
|
-
rebuildDisplayList();
|
|
751
|
-
if (selectedIndex >= displayList.length) {
|
|
752
|
-
selectedIndex = Math.max(0, displayList.length - 1);
|
|
753
|
-
}
|
|
850
|
+
refreshListings();
|
|
851
|
+
resetSelection();
|
|
754
852
|
pendingDeletionId = null;
|
|
755
853
|
} catch (err) {
|
|
756
854
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -760,6 +858,54 @@ async function runSessionBrowser(
|
|
|
760
858
|
tui.requestRender();
|
|
761
859
|
}
|
|
762
860
|
|
|
861
|
+
function makeRow(content: string, innerW: number, border: string): string {
|
|
862
|
+
const line = truncateToWidth(content, innerW);
|
|
863
|
+
const pad = Math.max(0, innerW - visibleWidth(line));
|
|
864
|
+
return `${border} ${line}${' '.repeat(pad)} ${border}`;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function renderTabs(innerW: number, border: string): string {
|
|
868
|
+
const sessions = tab === 'sessions' ? theme.fg('accent', '[Sessions]') : '[Sessions]';
|
|
869
|
+
const settings = tab === 'settings' ? theme.fg('accent', '[Settings]') : '[Settings]';
|
|
870
|
+
return makeRow(`${sessions} ${settings}`, innerW, border);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function renderSettings(width: number, innerW: number, border: string): string[] {
|
|
874
|
+
const borderFg = (s: string) => theme.fg('border', s);
|
|
875
|
+
const accent = (s: string) => theme.fg('accent', s);
|
|
876
|
+
const dim = (s: string) => theme.fg('dim', s);
|
|
877
|
+
const lines: string[] = [];
|
|
878
|
+
const title = accent(' Goosedump Dashboard ');
|
|
879
|
+
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
880
|
+
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
881
|
+
lines.push(renderTabs(innerW, border));
|
|
882
|
+
lines.push(
|
|
883
|
+
makeRow(
|
|
884
|
+
dim('Settings are written to ~/.pi/agent/settings.json and .pi/settings.json.'),
|
|
885
|
+
innerW,
|
|
886
|
+
border,
|
|
887
|
+
),
|
|
888
|
+
);
|
|
889
|
+
lines.push(makeRow('', innerW, border));
|
|
890
|
+
for (const line of settingsList.render(innerW)) lines.push(makeRow(line, innerW, border));
|
|
891
|
+
lines.push(makeRow('', innerW, border));
|
|
892
|
+
const effective = resolveGoosedumpSettings(location);
|
|
893
|
+
lines.push(
|
|
894
|
+
makeRow(
|
|
895
|
+
dim(
|
|
896
|
+
`Effective: steps=${formatCompactSetting(effective.stepsBeforeCompact)} turns=${formatCompactSetting(effective.turnsBeforeCompact)}`,
|
|
897
|
+
),
|
|
898
|
+
innerW,
|
|
899
|
+
border,
|
|
900
|
+
),
|
|
901
|
+
);
|
|
902
|
+
lines.push(
|
|
903
|
+
makeRow(dim('tab switch ↑↓ select enter/space cycle esc close'), innerW, border),
|
|
904
|
+
);
|
|
905
|
+
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
906
|
+
return lines;
|
|
907
|
+
}
|
|
908
|
+
|
|
763
909
|
return {
|
|
764
910
|
render(width: number): string[] {
|
|
765
911
|
const innerW = width - 4;
|
|
@@ -770,21 +916,17 @@ async function runSessionBrowser(
|
|
|
770
916
|
const text = (s: string) => theme.fg('text', s);
|
|
771
917
|
const muted = (s: string) => theme.fg('muted', s);
|
|
772
918
|
|
|
773
|
-
const lines: string[] = [];
|
|
774
|
-
|
|
775
919
|
if (mode === 'compact') {
|
|
776
|
-
const
|
|
920
|
+
const lines: string[] = [];
|
|
921
|
+
const title = accent(` Compact: ${truncateToWidth(compactTitle, 48)} `);
|
|
777
922
|
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
778
923
|
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
779
924
|
|
|
780
925
|
const total = compactLines.length;
|
|
781
926
|
const window = compactLines.slice(compactScroll, compactScroll + COMPACT_VIEWPORT);
|
|
782
|
-
for (const row of window)
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
for (let i = window.length; i < COMPACT_VIEWPORT; i++) {
|
|
927
|
+
for (const row of window) lines.push(makeRow(text(row), innerW, border));
|
|
928
|
+
for (let i = window.length; i < COMPACT_VIEWPORT; i++)
|
|
786
929
|
lines.push(makeRow('', innerW, border));
|
|
787
|
-
}
|
|
788
930
|
|
|
789
931
|
const position =
|
|
790
932
|
total > COMPACT_VIEWPORT
|
|
@@ -796,85 +938,77 @@ async function runSessionBrowser(
|
|
|
796
938
|
return lines;
|
|
797
939
|
}
|
|
798
940
|
|
|
799
|
-
|
|
800
|
-
|
|
941
|
+
if (tab === 'settings') return renderSettings(width, innerW, border);
|
|
942
|
+
|
|
943
|
+
const currentListings = locationListings();
|
|
944
|
+
const lines: string[] = [];
|
|
945
|
+
const title = accent(' Goosedump Dashboard ');
|
|
801
946
|
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
802
947
|
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
948
|
+
lines.push(renderTabs(innerW, border));
|
|
949
|
+
lines.push(
|
|
950
|
+
makeRow(muted(`Location: ${shortPath(location, innerW - 10)}`), innerW, border),
|
|
951
|
+
);
|
|
952
|
+
lines.push(
|
|
953
|
+
makeRow(
|
|
954
|
+
muted(
|
|
955
|
+
`${currentListings.length} session${currentListings.length === 1 ? '' : 's'} here`,
|
|
956
|
+
),
|
|
957
|
+
innerW,
|
|
958
|
+
border,
|
|
959
|
+
),
|
|
960
|
+
);
|
|
808
961
|
lines.push(makeRow('', innerW, border));
|
|
809
962
|
|
|
810
|
-
|
|
811
|
-
const total = displayList.length;
|
|
812
|
-
const window = displayList.slice(listScroll, listScroll + LIST_VIEWPORT);
|
|
963
|
+
const window = currentListings.slice(listScroll, listScroll + LIST_VIEWPORT);
|
|
813
964
|
for (let i = 0; i < window.length; i++) {
|
|
814
965
|
const absolute = listScroll + i;
|
|
815
|
-
const
|
|
966
|
+
const listing = window[i];
|
|
816
967
|
const isSelected = absolute === selectedIndex;
|
|
817
|
-
const deletePending =
|
|
818
|
-
|
|
819
|
-
const cursor = deletePending ? dim('␡ ') : isSelected ? accent('▶') : ' ';
|
|
820
|
-
const indent = ' '.repeat(entry.depth * 2);
|
|
821
|
-
const toggle = entry.hasChildren
|
|
822
|
-
? expandedNodes.has(entry.listing.id)
|
|
823
|
-
? muted('−')
|
|
824
|
-
: muted('+')
|
|
825
|
-
: ' ';
|
|
968
|
+
const deletePending = pendingDeletionId !== null && listing.id === pendingDeletionId;
|
|
969
|
+
const cursor = deletePending ? dim('␡') : isSelected ? accent('▶') : ' ';
|
|
826
970
|
const idText = deletePending
|
|
827
|
-
? dim(
|
|
971
|
+
? dim(listing.id)
|
|
828
972
|
: isSelected
|
|
829
|
-
? accent(
|
|
830
|
-
: text(
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const cwdText = deletePending
|
|
837
|
-
? muted(cwdTruncated)
|
|
838
|
-
: isSelected
|
|
839
|
-
? accent(cwdTruncated)
|
|
840
|
-
: muted(cwdTruncated);
|
|
841
|
-
|
|
842
|
-
const line = ` ${cursor}${indent}${toggle} ${idText} ${cwdText}`;
|
|
843
|
-
lines.push(makeRow(line, innerW, border));
|
|
973
|
+
? accent(listing.id)
|
|
974
|
+
: text(listing.id);
|
|
975
|
+
const label = listing.label ? ` ${listing.label}` : '';
|
|
976
|
+
const count = muted(
|
|
977
|
+
` (${listing.provider}, ${listing.path ? 'resumable' : 'no file'})`,
|
|
978
|
+
);
|
|
979
|
+
lines.push(makeRow(` ${cursor} ${idText}${muted(label)}${count}`, innerW, border));
|
|
844
980
|
}
|
|
845
981
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
982
|
+
for (let i = window.length; i < LIST_VIEWPORT; i++)
|
|
983
|
+
lines.push(makeRow('', innerW, border));
|
|
984
|
+
|
|
985
|
+
if (currentListings.length > LIST_VIEWPORT) {
|
|
986
|
+
const position = `${listScroll + 1}-${Math.min(listScroll + LIST_VIEWPORT, currentListings.length)} of ${currentListings.length}`;
|
|
987
|
+
lines.push(makeRow(dim(`(${position})`), innerW, border));
|
|
849
988
|
}
|
|
850
989
|
|
|
851
|
-
// Deletion confirmation
|
|
852
990
|
if (pendingDeletionId) {
|
|
853
991
|
lines.push(makeRow('', innerW, border));
|
|
854
992
|
lines.push(
|
|
855
993
|
makeRow(
|
|
856
|
-
dim(`
|
|
994
|
+
dim(`Delete session ${pendingDeletionId}? enter=confirm esc=cancel`),
|
|
857
995
|
innerW,
|
|
858
996
|
border,
|
|
859
997
|
),
|
|
860
998
|
);
|
|
861
999
|
}
|
|
862
1000
|
|
|
863
|
-
// Footer
|
|
864
1001
|
lines.push(makeRow('', innerW, border));
|
|
865
1002
|
lines.push(
|
|
866
1003
|
makeRow(
|
|
867
1004
|
dim(
|
|
868
|
-
'↑↓ navigate
|
|
1005
|
+
'tab settings ↑↓ navigate enter compact shift+enter resume ctrl+d delete esc close',
|
|
869
1006
|
),
|
|
870
1007
|
innerW,
|
|
871
1008
|
border,
|
|
872
1009
|
),
|
|
873
1010
|
);
|
|
874
|
-
|
|
875
|
-
// Bottom border
|
|
876
1011
|
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
877
|
-
|
|
878
1012
|
return lines;
|
|
879
1013
|
},
|
|
880
1014
|
|
|
@@ -909,11 +1043,29 @@ async function runSessionBrowser(
|
|
|
909
1043
|
return;
|
|
910
1044
|
}
|
|
911
1045
|
|
|
1046
|
+
if (matchesKey(data, Key.tab) || matchesKey(data, Key.shift(Key.tab))) {
|
|
1047
|
+
tab = tab === 'sessions' ? 'settings' : 'sessions';
|
|
1048
|
+
if (tab === 'settings') rebuildSettingsList();
|
|
1049
|
+
tui.requestRender();
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (tab === 'settings') {
|
|
1054
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
1055
|
+
done(undefined);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
settingsList.handleInput(data);
|
|
1059
|
+
tui.requestRender();
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
912
1063
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
913
1064
|
done(undefined);
|
|
914
1065
|
return;
|
|
915
1066
|
}
|
|
916
1067
|
|
|
1068
|
+
const currentListings = locationListings();
|
|
917
1069
|
if (matchesKey(data, Key.up)) {
|
|
918
1070
|
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
919
1071
|
if (selectedIndex < listScroll) listScroll = selectedIndex;
|
|
@@ -922,7 +1074,7 @@ async function runSessionBrowser(
|
|
|
922
1074
|
}
|
|
923
1075
|
|
|
924
1076
|
if (matchesKey(data, Key.down)) {
|
|
925
|
-
selectedIndex = Math.min(
|
|
1077
|
+
selectedIndex = Math.min(Math.max(0, currentListings.length - 1), selectedIndex + 1);
|
|
926
1078
|
if (selectedIndex >= listScroll + LIST_VIEWPORT) {
|
|
927
1079
|
listScroll = selectedIndex - LIST_VIEWPORT + 1;
|
|
928
1080
|
}
|
|
@@ -930,7 +1082,6 @@ async function runSessionBrowser(
|
|
|
930
1082
|
return;
|
|
931
1083
|
}
|
|
932
1084
|
|
|
933
|
-
// Deletion confirmation: enter confirms, esc cancels
|
|
934
1085
|
if (pendingDeletionId) {
|
|
935
1086
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
936
1087
|
pendingDeletionId = null;
|
|
@@ -944,47 +1095,21 @@ async function runSessionBrowser(
|
|
|
944
1095
|
return;
|
|
945
1096
|
}
|
|
946
1097
|
|
|
947
|
-
// Ctrl+D marks session for deletion
|
|
948
1098
|
if (matchesKey(data, Key.ctrl('d'))) {
|
|
949
|
-
const listing =
|
|
1099
|
+
const listing = currentListings[selectedIndex];
|
|
950
1100
|
if (listing) pendingDeletionId = listing.id;
|
|
951
1101
|
tui.requestRender();
|
|
952
1102
|
return;
|
|
953
1103
|
}
|
|
954
1104
|
|
|
955
|
-
// Expand/collapse: space, +, or - key
|
|
956
|
-
const entry = displayList[selectedIndex];
|
|
957
|
-
if (entry && entry.hasChildren && (data === ' ' || data === '+' || data === '-')) {
|
|
958
|
-
const id = entry.listing.id;
|
|
959
|
-
if (data === '+') {
|
|
960
|
-
expandedNodes.add(id);
|
|
961
|
-
} else if (data === '-') {
|
|
962
|
-
expandedNodes.delete(id);
|
|
963
|
-
} else {
|
|
964
|
-
// space = toggle
|
|
965
|
-
if (expandedNodes.has(id)) {
|
|
966
|
-
expandedNodes.delete(id);
|
|
967
|
-
} else {
|
|
968
|
-
expandedNodes.add(id);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
rebuildDisplayList();
|
|
972
|
-
// Keep selection on the same entry
|
|
973
|
-
const newIndex = displayList.findIndex((d) => d.listing.id === id);
|
|
974
|
-
if (newIndex >= 0) selectedIndex = newIndex;
|
|
975
|
-
if (selectedIndex < listScroll) listScroll = selectedIndex;
|
|
976
|
-
tui.requestRender();
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
1105
|
if (matchesKey(data, Key.shift(Key.enter))) {
|
|
981
|
-
resumePath =
|
|
1106
|
+
resumePath = currentListings[selectedIndex]?.path;
|
|
982
1107
|
done(undefined);
|
|
983
1108
|
return;
|
|
984
1109
|
}
|
|
985
1110
|
|
|
986
1111
|
if (matchesKey(data, Key.enter) || matchesKey(data, Key.return)) {
|
|
987
|
-
const listing =
|
|
1112
|
+
const listing = currentListings[selectedIndex];
|
|
988
1113
|
if (!listing) return;
|
|
989
1114
|
compactTitle = listing.id;
|
|
990
1115
|
compactScroll = 0;
|
|
@@ -999,18 +1124,11 @@ async function runSessionBrowser(
|
|
|
999
1124
|
}
|
|
1000
1125
|
mode = 'compact';
|
|
1001
1126
|
tui.requestRender();
|
|
1002
|
-
return;
|
|
1003
1127
|
}
|
|
1004
1128
|
},
|
|
1005
1129
|
|
|
1006
1130
|
invalidate(): void {},
|
|
1007
1131
|
};
|
|
1008
|
-
|
|
1009
|
-
function makeRow(content: string, innerW: number, border: string): string {
|
|
1010
|
-
const line = truncateToWidth(content, innerW);
|
|
1011
|
-
const pad = Math.max(0, innerW - visibleWidth(line));
|
|
1012
|
-
return `${border} ${line}${' '.repeat(pad)} ${border}`;
|
|
1013
|
-
}
|
|
1014
1132
|
},
|
|
1015
1133
|
{
|
|
1016
1134
|
overlay: true,
|
|
@@ -1032,6 +1150,64 @@ export function createGoosedumpIntegration(
|
|
|
1032
1150
|
const shouldOverrideDefaultCompaction = options.overrideDefaultCompaction ?? true;
|
|
1033
1151
|
|
|
1034
1152
|
let goosedumpAvailable = false;
|
|
1153
|
+
let compactSettings = DEFAULT_GOOSEDUMP_SETTINGS;
|
|
1154
|
+
let settingsSignature = '';
|
|
1155
|
+
let stepsRemaining = 0;
|
|
1156
|
+
let turnsRemaining = 0;
|
|
1157
|
+
let counterTriggered = false;
|
|
1158
|
+
let compactionQueued = false;
|
|
1159
|
+
|
|
1160
|
+
function compactSettingsSignature(settings: GoosedumpSettings): string {
|
|
1161
|
+
return `${settings.stepsBeforeCompact}:${settings.turnsBeforeCompact}`;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
function resetCompactCounters(cwd: string): void {
|
|
1165
|
+
compactSettings = resolveGoosedumpSettings(cwd);
|
|
1166
|
+
settingsSignature = compactSettingsSignature(compactSettings);
|
|
1167
|
+
stepsRemaining = compactSettings.stepsBeforeCompact;
|
|
1168
|
+
turnsRemaining = compactSettings.turnsBeforeCompact;
|
|
1169
|
+
counterTriggered = false;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
function reloadCompactSettings(cwd: string): void {
|
|
1173
|
+
const next = resolveGoosedumpSettings(cwd);
|
|
1174
|
+
const nextSignature = compactSettingsSignature(next);
|
|
1175
|
+
if (nextSignature !== settingsSignature) resetCompactCounters(cwd);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function noteCompletedStep(ctx: ExtensionContext): void {
|
|
1179
|
+
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction) return;
|
|
1180
|
+
reloadCompactSettings(ctx.cwd);
|
|
1181
|
+
if (compactSettings.stepsBeforeCompact === 0) return;
|
|
1182
|
+
|
|
1183
|
+
stepsRemaining -= 1;
|
|
1184
|
+
if (stepsRemaining <= 0) counterTriggered = true;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
function noteCompletedTurn(ctx: ExtensionContext): void {
|
|
1188
|
+
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction) return;
|
|
1189
|
+
reloadCompactSettings(ctx.cwd);
|
|
1190
|
+
if (compactSettings.turnsBeforeCompact > 0) {
|
|
1191
|
+
turnsRemaining -= 1;
|
|
1192
|
+
if (turnsRemaining <= 0) counterTriggered = true;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (!counterTriggered || compactionQueued) return;
|
|
1196
|
+
|
|
1197
|
+
resetCompactCounters(ctx.cwd);
|
|
1198
|
+
compactionQueued = true;
|
|
1199
|
+
ctx.compact({
|
|
1200
|
+
onComplete: () => {
|
|
1201
|
+
compactionQueued = false;
|
|
1202
|
+
resetCompactCounters(ctx.cwd);
|
|
1203
|
+
},
|
|
1204
|
+
onError: (error) => {
|
|
1205
|
+
compactionQueued = false;
|
|
1206
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1207
|
+
if (ctx.hasUI) ctx.ui.notify(`goosedump compact failed: ${message}`, 'error');
|
|
1208
|
+
},
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1035
1211
|
|
|
1036
1212
|
function checkGoosedump(ctx?: ExtensionContext): boolean {
|
|
1037
1213
|
const version = goosedumpVersion();
|
|
@@ -1323,6 +1499,7 @@ export function createGoosedumpIntegration(
|
|
|
1323
1499
|
|
|
1324
1500
|
pi.on('session_start', async (_event, ctx) => {
|
|
1325
1501
|
goosedumpAvailable = checkGoosedump(ctx);
|
|
1502
|
+
resetCompactCounters(ctx.cwd);
|
|
1326
1503
|
|
|
1327
1504
|
if (goosedumpAvailable) {
|
|
1328
1505
|
const theme = ctx.ui.theme;
|
|
@@ -1332,6 +1509,14 @@ export function createGoosedumpIntegration(
|
|
|
1332
1509
|
}
|
|
1333
1510
|
});
|
|
1334
1511
|
|
|
1512
|
+
pi.on('tool_execution_end', async (_event, ctx) => {
|
|
1513
|
+
noteCompletedStep(ctx);
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
pi.on('turn_end', async (_event, ctx) => {
|
|
1517
|
+
noteCompletedTurn(ctx);
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1335
1520
|
pi.on('session_before_compact', async (event, ctx) => {
|
|
1336
1521
|
if (!shouldOverrideDefaultCompaction || !goosedumpAvailable) return;
|
|
1337
1522
|
if (event.customInstructions?.trim()) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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.8.1",
|
|
33
33
|
"@sinclair/typebox": "^0.34.49"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|