pi-goosedump 0.7.2 → 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 -215
- 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,27 +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
|
-
if (idx >= 0) listings.splice(idx, 1);
|
|
750
|
-
rebuildTree();
|
|
751
|
-
rebuildDisplayList();
|
|
752
|
-
if (selectedIndex >= displayList.length) {
|
|
753
|
-
selectedIndex = Math.max(0, displayList.length - 1);
|
|
754
|
-
}
|
|
850
|
+
refreshListings();
|
|
851
|
+
resetSelection();
|
|
755
852
|
pendingDeletionId = null;
|
|
756
853
|
} catch (err) {
|
|
757
854
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -761,6 +858,54 @@ async function runSessionBrowser(
|
|
|
761
858
|
tui.requestRender();
|
|
762
859
|
}
|
|
763
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
|
+
|
|
764
909
|
return {
|
|
765
910
|
render(width: number): string[] {
|
|
766
911
|
const innerW = width - 4;
|
|
@@ -771,21 +916,17 @@ async function runSessionBrowser(
|
|
|
771
916
|
const text = (s: string) => theme.fg('text', s);
|
|
772
917
|
const muted = (s: string) => theme.fg('muted', s);
|
|
773
918
|
|
|
774
|
-
const lines: string[] = [];
|
|
775
|
-
|
|
776
919
|
if (mode === 'compact') {
|
|
777
|
-
const
|
|
920
|
+
const lines: string[] = [];
|
|
921
|
+
const title = accent(` Compact: ${truncateToWidth(compactTitle, 48)} `);
|
|
778
922
|
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
779
923
|
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
780
924
|
|
|
781
925
|
const total = compactLines.length;
|
|
782
926
|
const window = compactLines.slice(compactScroll, compactScroll + COMPACT_VIEWPORT);
|
|
783
|
-
for (const row of window)
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
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++)
|
|
787
929
|
lines.push(makeRow('', innerW, border));
|
|
788
|
-
}
|
|
789
930
|
|
|
790
931
|
const position =
|
|
791
932
|
total > COMPACT_VIEWPORT
|
|
@@ -797,85 +938,77 @@ async function runSessionBrowser(
|
|
|
797
938
|
return lines;
|
|
798
939
|
}
|
|
799
940
|
|
|
800
|
-
|
|
801
|
-
|
|
941
|
+
if (tab === 'settings') return renderSettings(width, innerW, border);
|
|
942
|
+
|
|
943
|
+
const currentListings = locationListings();
|
|
944
|
+
const lines: string[] = [];
|
|
945
|
+
const title = accent(' Goosedump Dashboard ');
|
|
802
946
|
const topFill = borderFg('─'.repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
803
947
|
lines.push(`${borderFg('╭─')}${title}${topFill}${borderFg('─╮')}`);
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
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
|
+
);
|
|
809
961
|
lines.push(makeRow('', innerW, border));
|
|
810
962
|
|
|
811
|
-
|
|
812
|
-
const total = displayList.length;
|
|
813
|
-
const window = displayList.slice(listScroll, listScroll + LIST_VIEWPORT);
|
|
963
|
+
const window = currentListings.slice(listScroll, listScroll + LIST_VIEWPORT);
|
|
814
964
|
for (let i = 0; i < window.length; i++) {
|
|
815
965
|
const absolute = listScroll + i;
|
|
816
|
-
const
|
|
966
|
+
const listing = window[i];
|
|
817
967
|
const isSelected = absolute === selectedIndex;
|
|
818
|
-
const deletePending =
|
|
819
|
-
|
|
820
|
-
const cursor = deletePending ? dim('␡ ') : isSelected ? accent('▶') : ' ';
|
|
821
|
-
const indent = ' '.repeat(entry.depth * 2);
|
|
822
|
-
const toggle = entry.hasChildren
|
|
823
|
-
? expandedNodes.has(entry.listing.id)
|
|
824
|
-
? muted('−')
|
|
825
|
-
: muted('+')
|
|
826
|
-
: ' ';
|
|
968
|
+
const deletePending = pendingDeletionId !== null && listing.id === pendingDeletionId;
|
|
969
|
+
const cursor = deletePending ? dim('␡') : isSelected ? accent('▶') : ' ';
|
|
827
970
|
const idText = deletePending
|
|
828
|
-
? dim(
|
|
971
|
+
? dim(listing.id)
|
|
829
972
|
: isSelected
|
|
830
|
-
? accent(
|
|
831
|
-
: text(
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
const cwdText = deletePending
|
|
838
|
-
? muted(cwdTruncated)
|
|
839
|
-
: isSelected
|
|
840
|
-
? accent(cwdTruncated)
|
|
841
|
-
: muted(cwdTruncated);
|
|
842
|
-
|
|
843
|
-
const line = ` ${cursor}${indent}${toggle} ${idText} ${cwdText}`;
|
|
844
|
-
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));
|
|
845
980
|
}
|
|
846
981
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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));
|
|
850
988
|
}
|
|
851
989
|
|
|
852
|
-
// Deletion confirmation
|
|
853
990
|
if (pendingDeletionId) {
|
|
854
991
|
lines.push(makeRow('', innerW, border));
|
|
855
992
|
lines.push(
|
|
856
993
|
makeRow(
|
|
857
|
-
dim(`
|
|
994
|
+
dim(`Delete session ${pendingDeletionId}? enter=confirm esc=cancel`),
|
|
858
995
|
innerW,
|
|
859
996
|
border,
|
|
860
997
|
),
|
|
861
998
|
);
|
|
862
999
|
}
|
|
863
1000
|
|
|
864
|
-
// Footer
|
|
865
1001
|
lines.push(makeRow('', innerW, border));
|
|
866
1002
|
lines.push(
|
|
867
1003
|
makeRow(
|
|
868
1004
|
dim(
|
|
869
|
-
'↑↓ navigate
|
|
1005
|
+
'tab settings ↑↓ navigate enter compact shift+enter resume ctrl+d delete esc close',
|
|
870
1006
|
),
|
|
871
1007
|
innerW,
|
|
872
1008
|
border,
|
|
873
1009
|
),
|
|
874
1010
|
);
|
|
875
|
-
|
|
876
|
-
// Bottom border
|
|
877
1011
|
lines.push(`${borderFg('╰')}${borderFg('─'.repeat(width - 2))}${borderFg('╯')}`);
|
|
878
|
-
|
|
879
1012
|
return lines;
|
|
880
1013
|
},
|
|
881
1014
|
|
|
@@ -910,11 +1043,29 @@ async function runSessionBrowser(
|
|
|
910
1043
|
return;
|
|
911
1044
|
}
|
|
912
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
|
+
|
|
913
1063
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
914
1064
|
done(undefined);
|
|
915
1065
|
return;
|
|
916
1066
|
}
|
|
917
1067
|
|
|
1068
|
+
const currentListings = locationListings();
|
|
918
1069
|
if (matchesKey(data, Key.up)) {
|
|
919
1070
|
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
920
1071
|
if (selectedIndex < listScroll) listScroll = selectedIndex;
|
|
@@ -923,7 +1074,7 @@ async function runSessionBrowser(
|
|
|
923
1074
|
}
|
|
924
1075
|
|
|
925
1076
|
if (matchesKey(data, Key.down)) {
|
|
926
|
-
selectedIndex = Math.min(
|
|
1077
|
+
selectedIndex = Math.min(Math.max(0, currentListings.length - 1), selectedIndex + 1);
|
|
927
1078
|
if (selectedIndex >= listScroll + LIST_VIEWPORT) {
|
|
928
1079
|
listScroll = selectedIndex - LIST_VIEWPORT + 1;
|
|
929
1080
|
}
|
|
@@ -931,7 +1082,6 @@ async function runSessionBrowser(
|
|
|
931
1082
|
return;
|
|
932
1083
|
}
|
|
933
1084
|
|
|
934
|
-
// Deletion confirmation: enter confirms, esc cancels
|
|
935
1085
|
if (pendingDeletionId) {
|
|
936
1086
|
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) {
|
|
937
1087
|
pendingDeletionId = null;
|
|
@@ -945,47 +1095,21 @@ async function runSessionBrowser(
|
|
|
945
1095
|
return;
|
|
946
1096
|
}
|
|
947
1097
|
|
|
948
|
-
// Ctrl+D marks session for deletion
|
|
949
1098
|
if (matchesKey(data, Key.ctrl('d'))) {
|
|
950
|
-
const listing =
|
|
1099
|
+
const listing = currentListings[selectedIndex];
|
|
951
1100
|
if (listing) pendingDeletionId = listing.id;
|
|
952
1101
|
tui.requestRender();
|
|
953
1102
|
return;
|
|
954
1103
|
}
|
|
955
1104
|
|
|
956
|
-
// Expand/collapse: space, +, or - key
|
|
957
|
-
const entry = displayList[selectedIndex];
|
|
958
|
-
if (entry && entry.hasChildren && (data === ' ' || data === '+' || data === '-')) {
|
|
959
|
-
const id = entry.listing.id;
|
|
960
|
-
if (data === '+') {
|
|
961
|
-
expandedNodes.add(id);
|
|
962
|
-
} else if (data === '-') {
|
|
963
|
-
expandedNodes.delete(id);
|
|
964
|
-
} else {
|
|
965
|
-
// space = toggle
|
|
966
|
-
if (expandedNodes.has(id)) {
|
|
967
|
-
expandedNodes.delete(id);
|
|
968
|
-
} else {
|
|
969
|
-
expandedNodes.add(id);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
rebuildDisplayList();
|
|
973
|
-
// Keep selection on the same entry
|
|
974
|
-
const newIndex = displayList.findIndex((d) => d.listing.id === id);
|
|
975
|
-
if (newIndex >= 0) selectedIndex = newIndex;
|
|
976
|
-
if (selectedIndex < listScroll) listScroll = selectedIndex;
|
|
977
|
-
tui.requestRender();
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
1105
|
if (matchesKey(data, Key.shift(Key.enter))) {
|
|
982
|
-
resumePath =
|
|
1106
|
+
resumePath = currentListings[selectedIndex]?.path;
|
|
983
1107
|
done(undefined);
|
|
984
1108
|
return;
|
|
985
1109
|
}
|
|
986
1110
|
|
|
987
1111
|
if (matchesKey(data, Key.enter) || matchesKey(data, Key.return)) {
|
|
988
|
-
const listing =
|
|
1112
|
+
const listing = currentListings[selectedIndex];
|
|
989
1113
|
if (!listing) return;
|
|
990
1114
|
compactTitle = listing.id;
|
|
991
1115
|
compactScroll = 0;
|
|
@@ -1000,18 +1124,11 @@ async function runSessionBrowser(
|
|
|
1000
1124
|
}
|
|
1001
1125
|
mode = 'compact';
|
|
1002
1126
|
tui.requestRender();
|
|
1003
|
-
return;
|
|
1004
1127
|
}
|
|
1005
1128
|
},
|
|
1006
1129
|
|
|
1007
1130
|
invalidate(): void {},
|
|
1008
1131
|
};
|
|
1009
|
-
|
|
1010
|
-
function makeRow(content: string, innerW: number, border: string): string {
|
|
1011
|
-
const line = truncateToWidth(content, innerW);
|
|
1012
|
-
const pad = Math.max(0, innerW - visibleWidth(line));
|
|
1013
|
-
return `${border} ${line}${' '.repeat(pad)} ${border}`;
|
|
1014
|
-
}
|
|
1015
1132
|
},
|
|
1016
1133
|
{
|
|
1017
1134
|
overlay: true,
|
|
@@ -1033,6 +1150,64 @@ export function createGoosedumpIntegration(
|
|
|
1033
1150
|
const shouldOverrideDefaultCompaction = options.overrideDefaultCompaction ?? true;
|
|
1034
1151
|
|
|
1035
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
|
+
}
|
|
1036
1211
|
|
|
1037
1212
|
function checkGoosedump(ctx?: ExtensionContext): boolean {
|
|
1038
1213
|
const version = goosedumpVersion();
|
|
@@ -1324,6 +1499,7 @@ export function createGoosedumpIntegration(
|
|
|
1324
1499
|
|
|
1325
1500
|
pi.on('session_start', async (_event, ctx) => {
|
|
1326
1501
|
goosedumpAvailable = checkGoosedump(ctx);
|
|
1502
|
+
resetCompactCounters(ctx.cwd);
|
|
1327
1503
|
|
|
1328
1504
|
if (goosedumpAvailable) {
|
|
1329
1505
|
const theme = ctx.ui.theme;
|
|
@@ -1333,6 +1509,14 @@ export function createGoosedumpIntegration(
|
|
|
1333
1509
|
}
|
|
1334
1510
|
});
|
|
1335
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
|
+
|
|
1336
1520
|
pi.on('session_before_compact', async (event, ctx) => {
|
|
1337
1521
|
if (!shouldOverrideDefaultCompaction || !goosedumpAvailable) return;
|
|
1338
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": {
|