pi-goosedump 0.12.0 → 0.12.1
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 +16 -7
- package/dist/index.ts +394 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,11 +27,13 @@ report that they are unavailable.
|
|
|
27
27
|
provenance within a 4,096-byte/token-safe output budget.
|
|
28
28
|
- **`goose_compact`** starts asynchronous compaction of the current Pi session.
|
|
29
29
|
Set `keep` to preserve the last N user turns verbatim.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
- **`goose_sessions`** lists or deletes saved sessions scoped to the current working directory. Deleting the active session is safety-blocked.
|
|
31
|
+
- **`goose_forget`** deletes stored durable memory by content hash or provider:id. Writes durable tombstones to prevent re-learning.
|
|
32
|
+
- **`goose_memory_status`** inspects durable-memory counts and indexing health.
|
|
33
|
+
The session-history tools accept an optional `provider` (`pi` by default),
|
|
34
|
+
`session`, and `scope` (`lineage` by default or `all`). The current Pi session
|
|
35
|
+
is used when both provider and session are omitted. Non-Pi providers require an
|
|
36
|
+
explicit session ID. Targets sent to goosedump are always provider-qualified.
|
|
35
37
|
|
|
36
38
|
Examples:
|
|
37
39
|
|
|
@@ -43,6 +45,9 @@ goose_grep({ pattern: "*timeout*" })
|
|
|
43
45
|
goose_recall({ query: "prior architecture decision" })
|
|
44
46
|
goose_recall({ query: "database", type: "decision" })
|
|
45
47
|
goose_compact({ keep: 2 })
|
|
48
|
+
goose_sessions({ action: "list" })
|
|
49
|
+
goose_forget({ hash: "a1b2c3d4..." })
|
|
50
|
+
goose_memory_status({})
|
|
46
51
|
```
|
|
47
52
|
|
|
48
53
|
## Commands
|
|
@@ -51,6 +56,9 @@ goose_compact({ keep: 2 })
|
|
|
51
56
|
an inline prompt; selecting a result expands it.
|
|
52
57
|
- **`/goose-compact [keep:N]`** compacts now. With `keep:N`, the last N user
|
|
53
58
|
turns remain verbatim; without it, Pi chooses the cut point.
|
|
59
|
+
- **`/goose-sessions`** lists or deletes saved sessions scoped to the current working directory.
|
|
60
|
+
- **`/goose-forget [hash|provider:id]`** deletes stored durable memory by hash or session.
|
|
61
|
+
- **`/goose-memory-status`** displays durable-memory storage health and indexing counts.
|
|
54
62
|
- **`/goose-settings`** edits global and project compaction settings.
|
|
55
63
|
|
|
56
64
|
## Settings
|
|
@@ -70,8 +78,9 @@ Optional settings live under `goosedump` in:
|
|
|
70
78
|
|
|
71
79
|
`turnsBeforeCompact` is an integer from 0 to 255. `0` disables scheduled
|
|
72
80
|
compaction. The countdown is restored when a session resumes and appears in the
|
|
73
|
-
footer
|
|
74
|
-
|
|
81
|
+
footer with a status dot (green when >25% turns remain, yellow when ≤25%, red when ≤12.5%)
|
|
82
|
+
followed by hexadecimal turn countdown: `● GD: 0A` means ten turns remain, `● GD: 00` is ready
|
|
83
|
+
to compact, and `● GD: --` is disabled. Clearing the project value restores the
|
|
75
84
|
global setting.
|
|
76
85
|
|
|
77
86
|
## Compaction behavior
|
package/dist/index.ts
CHANGED
|
@@ -366,18 +366,6 @@ function goosedumpUnavailableResult() {
|
|
|
366
366
|
function compactStartedMessage(keep) {
|
|
367
367
|
return keep !== null ? `Compacting with goosedump; keeping the last ${keep} user turn${keep === 1 ? "" : "s"}.` : "Compacting with goosedump up to Pi's token-based cut.";
|
|
368
368
|
}
|
|
369
|
-
function runGoosedump(args) {
|
|
370
|
-
try {
|
|
371
|
-
return execFileSync(process.execPath, [resolveGoosedumpBinary(), ...args], {
|
|
372
|
-
encoding: "utf-8",
|
|
373
|
-
maxBuffer: 16 * 1024 * 1024
|
|
374
|
-
});
|
|
375
|
-
} catch (err) {
|
|
376
|
-
const stderr = err.stderr?.trim();
|
|
377
|
-
const message = stderr ?? (err instanceof Error ? err.message : String(err));
|
|
378
|
-
throw new Error(message);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
369
|
function runGoosedumpAsync(args, signal, executablePath = resolveNativeGoosedumpBinary()) {
|
|
382
370
|
return new Promise((resolve, reject) => {
|
|
383
371
|
execFile(executablePath, args, {
|
|
@@ -419,24 +407,27 @@ function appendScope(args, scope) {
|
|
|
419
407
|
if (scope === "all")
|
|
420
408
|
args.push("--all");
|
|
421
409
|
}
|
|
422
|
-
function goosedumpSearch(target, query, options = {}) {
|
|
410
|
+
async function goosedumpSearch(target, query, options = {}, signal, executablePath) {
|
|
423
411
|
const args = ["session", "search", target, query, "--as", "pi"];
|
|
424
412
|
appendScope(args, options.scope);
|
|
425
413
|
if (options.page)
|
|
426
414
|
args.push("--page", String(options.page));
|
|
427
|
-
|
|
415
|
+
const output = await runGoosedumpAsync(args, signal, executablePath);
|
|
416
|
+
return parsePiMessages(output.stdout);
|
|
428
417
|
}
|
|
429
|
-
function goosedumpGrep(target, pattern, options = {}) {
|
|
418
|
+
async function goosedumpGrep(target, pattern, options = {}, signal, executablePath) {
|
|
430
419
|
const args = ["session", "find", target, pattern, "--as", "pi"];
|
|
431
420
|
appendScope(args, options.scope);
|
|
432
|
-
|
|
421
|
+
const output = await runGoosedumpAsync(args, signal, executablePath);
|
|
422
|
+
return parsePiMessages(output.stdout);
|
|
433
423
|
}
|
|
434
|
-
function goosedumpShow(target, options = {}) {
|
|
424
|
+
async function goosedumpShow(target, options = {}, signal, executablePath) {
|
|
435
425
|
const args = ["session", "show", target, "--as", "pi"];
|
|
436
426
|
appendScope(args, options.scope);
|
|
437
427
|
for (const id of options.ids ?? [])
|
|
438
428
|
args.push("--entry", id);
|
|
439
|
-
|
|
429
|
+
const output = await runGoosedumpAsync(args, signal, executablePath);
|
|
430
|
+
return parsePiMessages(output.stdout);
|
|
440
431
|
}
|
|
441
432
|
async function goosedumpRecall(query, options = {}, signal, executablePath) {
|
|
442
433
|
const args = ["memory", "search", query, "--limit", String(options.limit ?? 5)];
|
|
@@ -449,6 +440,28 @@ async function goosedumpRecall(query, options = {}, signal, executablePath) {
|
|
|
449
440
|
const output = await runGoosedumpAsync(args, signal, executablePath);
|
|
450
441
|
return { hits: parseMemoryHits(output.stdout), warning: output.stderr.trim() };
|
|
451
442
|
}
|
|
443
|
+
async function goosedumpMemoryForget(target, signal, executablePath) {
|
|
444
|
+
const output = await runGoosedumpAsync(["memory", "forget", target], signal, executablePath);
|
|
445
|
+
return output.stdout.trim() || `Removed memory target: ${target}`;
|
|
446
|
+
}
|
|
447
|
+
async function goosedumpMemoryStatus(signal, executablePath) {
|
|
448
|
+
const output = await runGoosedumpAsync(["memory", "status"], signal, executablePath);
|
|
449
|
+
return output.stdout.trim();
|
|
450
|
+
}
|
|
451
|
+
async function goosedumpListSessions(signal, executablePath) {
|
|
452
|
+
const args = ["session", "list", "--json"];
|
|
453
|
+
const output = await runGoosedumpAsync(args, signal, executablePath);
|
|
454
|
+
try {
|
|
455
|
+
const parsed = JSON.parse(output.stdout);
|
|
456
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
457
|
+
} catch {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async function goosedumpRemoveSession(target, signal, executablePath) {
|
|
462
|
+
const args = ["session", "remove", target, "--yes"];
|
|
463
|
+
await runGoosedumpAsync(args, signal, executablePath);
|
|
464
|
+
}
|
|
452
465
|
function resolveTarget(ctx, provider, contextId) {
|
|
453
466
|
if (contextId)
|
|
454
467
|
return formatTarget(provider, contextId);
|
|
@@ -896,9 +909,9 @@ async function runSearchPanel(ctx, query) {
|
|
|
896
909
|
let scroll = 0;
|
|
897
910
|
let detailLines = [];
|
|
898
911
|
let detailScroll = 0;
|
|
899
|
-
function runSearch() {
|
|
912
|
+
async function runSearch() {
|
|
900
913
|
try {
|
|
901
|
-
const first = goosedumpSearch(target, query, { scope: "lineage", page: 1 });
|
|
914
|
+
const first = await goosedumpSearch(target, query, { scope: "lineage", page: 1 });
|
|
902
915
|
if (first.length === 0) {
|
|
903
916
|
entries = [];
|
|
904
917
|
error = `No results for "${query}"`;
|
|
@@ -908,7 +921,7 @@ async function runSearchPanel(ctx, query) {
|
|
|
908
921
|
}
|
|
909
922
|
const all = [...first];
|
|
910
923
|
for (let page = 2;page <= 10; page += 1) {
|
|
911
|
-
const result = goosedumpSearch(target, query, { scope: "lineage", page });
|
|
924
|
+
const result = await goosedumpSearch(target, query, { scope: "lineage", page });
|
|
912
925
|
if (result.length === 0)
|
|
913
926
|
break;
|
|
914
927
|
all.push(...result);
|
|
@@ -929,9 +942,9 @@ async function runSearchPanel(ctx, query) {
|
|
|
929
942
|
} else {
|
|
930
943
|
runSearch();
|
|
931
944
|
}
|
|
932
|
-
function loadDetail(id) {
|
|
945
|
+
async function loadDetail(id) {
|
|
933
946
|
try {
|
|
934
|
-
const messages = goosedumpShow(target, { ids: [id] });
|
|
947
|
+
const messages = await goosedumpShow(target, { ids: [id] });
|
|
935
948
|
const content = messages.length > 0 ? messages[0].content : "No content for this entry.";
|
|
936
949
|
detailLines = wrapTextWithAnsi(content, wrapWidth);
|
|
937
950
|
detailScroll = 0;
|
|
@@ -1071,6 +1084,176 @@ async function runSearchPanel(ctx, query) {
|
|
|
1071
1084
|
}
|
|
1072
1085
|
});
|
|
1073
1086
|
}
|
|
1087
|
+
async function runSessionsPanel(ctx, executablePath) {
|
|
1088
|
+
const OVERLAY_WIDTH = 84;
|
|
1089
|
+
const VIEWPORT = 18;
|
|
1090
|
+
await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
1091
|
+
const currentTarget = currentSessionTarget(ctx);
|
|
1092
|
+
const borderFg = (s) => theme.fg("border", s);
|
|
1093
|
+
const border = theme.fg("border", "│");
|
|
1094
|
+
const accent = (s) => theme.fg("accent", s);
|
|
1095
|
+
const text = (s) => theme.fg("text", s);
|
|
1096
|
+
const dim = (s) => theme.fg("dim", s);
|
|
1097
|
+
const muted = (s) => theme.fg("muted", s);
|
|
1098
|
+
let sessions = [];
|
|
1099
|
+
let error = null;
|
|
1100
|
+
let selected = 0;
|
|
1101
|
+
let scroll = 0;
|
|
1102
|
+
async function loadSessions() {
|
|
1103
|
+
try {
|
|
1104
|
+
const all = await goosedumpListSessions(undefined, executablePath);
|
|
1105
|
+
const cwdSessions = all.filter((s) => s.cwd === ctx.cwd);
|
|
1106
|
+
cwdSessions.sort((a, b) => {
|
|
1107
|
+
const timeA = a.from ? new Date(a.from).getTime() : 0;
|
|
1108
|
+
const timeB = b.from ? new Date(b.from).getTime() : 0;
|
|
1109
|
+
return timeB - timeA;
|
|
1110
|
+
});
|
|
1111
|
+
sessions = cwdSessions;
|
|
1112
|
+
if (sessions.length === 0) {
|
|
1113
|
+
error = "No sessions found for current directory.";
|
|
1114
|
+
} else {
|
|
1115
|
+
error = null;
|
|
1116
|
+
}
|
|
1117
|
+
selected = Math.min(selected, Math.max(0, sessions.length - 1));
|
|
1118
|
+
scroll = Math.min(scroll, Math.max(0, sessions.length - VIEWPORT));
|
|
1119
|
+
tui.requestRender();
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
sessions = [];
|
|
1122
|
+
error = err instanceof Error ? err.message : String(err);
|
|
1123
|
+
tui.requestRender();
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
loadSessions();
|
|
1127
|
+
async function deleteSelected() {
|
|
1128
|
+
const s = sessions[selected];
|
|
1129
|
+
if (!s)
|
|
1130
|
+
return;
|
|
1131
|
+
const target = formatTarget(s.provider, s.id);
|
|
1132
|
+
if (currentTarget === target) {
|
|
1133
|
+
ctx.ui.notify("Cannot delete the active session.", "error");
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
try {
|
|
1137
|
+
await goosedumpRemoveSession(target, undefined, executablePath);
|
|
1138
|
+
ctx.ui.notify(`Deleted session ${s.id}`, "info");
|
|
1139
|
+
await loadSessions();
|
|
1140
|
+
} catch (err) {
|
|
1141
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1142
|
+
ctx.ui.notify(`Failed to delete session: ${message}`, "error");
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return {
|
|
1146
|
+
render(width) {
|
|
1147
|
+
const innerW = width - 4;
|
|
1148
|
+
const lines = [];
|
|
1149
|
+
const title = accent(" Goosedump Sessions ");
|
|
1150
|
+
const topFill = borderFg("─".repeat(Math.max(0, width - 4 - visibleWidth(title))));
|
|
1151
|
+
lines.push(`${borderFg("╭─")}${title}${topFill}${borderFg("─╮")}`);
|
|
1152
|
+
if (error) {
|
|
1153
|
+
lines.push(makeRow(muted(error), innerW, border));
|
|
1154
|
+
lines.push(makeRow(dim("esc close"), innerW, border));
|
|
1155
|
+
lines.push(`${borderFg("╰")}${borderFg("─".repeat(width - 2))}${borderFg("╯")}`);
|
|
1156
|
+
return lines;
|
|
1157
|
+
}
|
|
1158
|
+
const maxScroll = Math.max(0, sessions.length - VIEWPORT);
|
|
1159
|
+
const clampScroll = Math.min(scroll, maxScroll);
|
|
1160
|
+
const window = sessions.slice(clampScroll, clampScroll + VIEWPORT);
|
|
1161
|
+
for (let i = 0;i < window.length; i++) {
|
|
1162
|
+
const absolute = clampScroll + i;
|
|
1163
|
+
const s = window[i];
|
|
1164
|
+
const isSelected = absolute === selected;
|
|
1165
|
+
const isActive = currentTarget === formatTarget(s.provider, s.id);
|
|
1166
|
+
const cursor = isSelected ? accent("▶") : " ";
|
|
1167
|
+
const activeMark = isActive ? accent("(active)") : "";
|
|
1168
|
+
const idText = isSelected ? accent(s.id) : text(s.id);
|
|
1169
|
+
const providerTag = muted(`[${s.provider}]`);
|
|
1170
|
+
const countTag = dim(`${s.count} entries`);
|
|
1171
|
+
const dateTag = s.from ? dim(new Date(s.from).toLocaleDateString()) : "";
|
|
1172
|
+
const rowContent = ` ${cursor} ${idText} ${providerTag} ${countTag} ${dateTag} ${activeMark}`.trimEnd();
|
|
1173
|
+
lines.push(makeRow(rowContent, innerW, border));
|
|
1174
|
+
}
|
|
1175
|
+
for (let i = window.length;i < VIEWPORT; i++)
|
|
1176
|
+
lines.push(makeRow("", innerW, border));
|
|
1177
|
+
const count = sessions.length;
|
|
1178
|
+
const position = count > VIEWPORT ? `${clampScroll + 1}-${Math.min(clampScroll + VIEWPORT, count)} of ${count}` : `${count} session${count === 1 ? "" : "s"}`;
|
|
1179
|
+
lines.push(makeRow("", innerW, border));
|
|
1180
|
+
lines.push(makeRow(dim(`↑↓ navigate enter resume d delete esc close (${position})`), innerW, border));
|
|
1181
|
+
lines.push(`${borderFg("╰")}${borderFg("─".repeat(width - 2))}${borderFg("╯")}`);
|
|
1182
|
+
return lines;
|
|
1183
|
+
},
|
|
1184
|
+
handleInput(data) {
|
|
1185
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c"))) {
|
|
1186
|
+
done(undefined);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (error && sessions.length === 0)
|
|
1190
|
+
return;
|
|
1191
|
+
const maxScroll = Math.max(0, sessions.length - VIEWPORT);
|
|
1192
|
+
if (matchesKey(data, Key.up)) {
|
|
1193
|
+
selected = Math.max(0, selected - 1);
|
|
1194
|
+
if (selected < scroll)
|
|
1195
|
+
scroll = selected;
|
|
1196
|
+
tui.requestRender();
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (matchesKey(data, Key.down)) {
|
|
1200
|
+
selected = Math.min(Math.max(0, sessions.length - 1), selected + 1);
|
|
1201
|
+
if (selected >= scroll + VIEWPORT)
|
|
1202
|
+
scroll = selected - VIEWPORT + 1;
|
|
1203
|
+
tui.requestRender();
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
if (matchesKey(data, Key.pageUp)) {
|
|
1207
|
+
selected = Math.max(0, selected - VIEWPORT);
|
|
1208
|
+
scroll = Math.max(0, Math.min(scroll, selected));
|
|
1209
|
+
tui.requestRender();
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
if (matchesKey(data, Key.pageDown)) {
|
|
1213
|
+
selected = Math.min(sessions.length - 1, selected + VIEWPORT);
|
|
1214
|
+
scroll = Math.min(maxScroll, Math.max(scroll, selected - VIEWPORT + 1));
|
|
1215
|
+
tui.requestRender();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (data === "d" || data === "D" || matchesKey(data, Key.delete)) {
|
|
1219
|
+
deleteSelected();
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
if (matchesKey(data, Key.enter) || matchesKey(data, Key.return)) {
|
|
1223
|
+
const s = sessions[selected];
|
|
1224
|
+
if (!s)
|
|
1225
|
+
return;
|
|
1226
|
+
if (s.provider !== "pi") {
|
|
1227
|
+
ctx.ui.notify(`Only Pi sessions can be resumed (session ${s.id} is ${s.provider})`, "warning");
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const sessionDir = ctx.sessionManager.getSessionDir();
|
|
1231
|
+
const sessionPath = join(sessionDir, s.id.endsWith(".jsonl") ? s.id : `${s.id}.jsonl`);
|
|
1232
|
+
if (!existsSync(sessionPath)) {
|
|
1233
|
+
ctx.ui.notify(`Session transcript file not found: ${sessionPath}`, "error");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
done(undefined);
|
|
1237
|
+
const cmdCtx = ctx;
|
|
1238
|
+
if (typeof cmdCtx.switchSession === "function") {
|
|
1239
|
+
cmdCtx.switchSession(sessionPath);
|
|
1240
|
+
} else {
|
|
1241
|
+
ctx.ui.notify(`Session switched to ${sessionPath}`, "info");
|
|
1242
|
+
}
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
},
|
|
1246
|
+
invalidate() {}
|
|
1247
|
+
};
|
|
1248
|
+
}, {
|
|
1249
|
+
overlay: true,
|
|
1250
|
+
overlayOptions: {
|
|
1251
|
+
anchor: "center",
|
|
1252
|
+
width: OVERLAY_WIDTH,
|
|
1253
|
+
margin: 2
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1074
1257
|
function createGoosedumpIntegration(options = {}) {
|
|
1075
1258
|
const shouldRegisterTool = options.registerTool ?? true;
|
|
1076
1259
|
const shouldOverrideDefaultCompaction = options.overrideDefaultCompaction ?? true;
|
|
@@ -1094,8 +1277,19 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1094
1277
|
if (!goosedumpAvailable)
|
|
1095
1278
|
return;
|
|
1096
1279
|
const active = shouldOverrideDefaultCompaction && compactSettings.turnsBeforeCompact > 0;
|
|
1280
|
+
let dotColor = "success";
|
|
1281
|
+
if (active) {
|
|
1282
|
+
const remainingRatio = turnsRemaining / compactSettings.turnsBeforeCompact;
|
|
1283
|
+
if (remainingRatio <= 0.125) {
|
|
1284
|
+
dotColor = "error";
|
|
1285
|
+
} else if (remainingRatio <= 0.25) {
|
|
1286
|
+
dotColor = "warning";
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const dot = ctx.ui.theme.fg(dotColor, "●");
|
|
1097
1290
|
const counter = active ? Math.max(0, turnsRemaining).toString(16).toUpperCase().padStart(2, "0") : "--";
|
|
1098
|
-
|
|
1291
|
+
const label = ctx.ui.theme.fg("text", `GD: ${counter}`);
|
|
1292
|
+
ctx.ui.setStatus("goosedump", `${dot} ${label}`);
|
|
1099
1293
|
}
|
|
1100
1294
|
function noteCompletedTurn(ctx) {
|
|
1101
1295
|
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
|
|
@@ -1186,6 +1380,56 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1186
1380
|
}
|
|
1187
1381
|
}
|
|
1188
1382
|
}));
|
|
1383
|
+
pi.registerTool(defineTool({
|
|
1384
|
+
name: "goose_forget",
|
|
1385
|
+
label: "goose_forget",
|
|
1386
|
+
description: "Forget/remove durable context entries from goosedump memory.",
|
|
1387
|
+
promptSnippet: "Remove obsolete entries from durable memory store",
|
|
1388
|
+
promptGuidelines: [
|
|
1389
|
+
"Use goose_forget to delete stale, inaccurate, or obsolete entries from durable memory.",
|
|
1390
|
+
"Requires a target memory ID or key."
|
|
1391
|
+
],
|
|
1392
|
+
parameters: Type.Object({
|
|
1393
|
+
target: Type.String({
|
|
1394
|
+
description: "Memory target/ID to forget (e.g. entry ID or key pattern)"
|
|
1395
|
+
})
|
|
1396
|
+
}),
|
|
1397
|
+
async execute(_id, params, signal, _onUpdate, _ctx) {
|
|
1398
|
+
if (!goosedumpAvailable)
|
|
1399
|
+
return goosedumpUnavailableResult();
|
|
1400
|
+
if (!params.target || params.target.trim().length === 0) {
|
|
1401
|
+
return textResult("goose_forget error: target is required");
|
|
1402
|
+
}
|
|
1403
|
+
try {
|
|
1404
|
+
const res = await goosedumpMemoryForget(params.target.trim(), signal, nativeExecutablePath);
|
|
1405
|
+
return textResult(res);
|
|
1406
|
+
} catch (err) {
|
|
1407
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1408
|
+
return textResult(`goosedump error: ${message}`);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}));
|
|
1412
|
+
pi.registerTool(defineTool({
|
|
1413
|
+
name: "goose_memory_status",
|
|
1414
|
+
label: "goose_memory_status",
|
|
1415
|
+
description: "Show durable memory status, indexing stats, and governance breakdown.",
|
|
1416
|
+
promptSnippet: "Inspect durable memory health and statistics",
|
|
1417
|
+
promptGuidelines: [
|
|
1418
|
+
"Use goose_memory_status to check memory index volume, health, and ingestion queue status."
|
|
1419
|
+
],
|
|
1420
|
+
parameters: Type.Object({}),
|
|
1421
|
+
async execute(_id, _params, signal, _onUpdate, _ctx) {
|
|
1422
|
+
if (!goosedumpAvailable)
|
|
1423
|
+
return goosedumpUnavailableResult();
|
|
1424
|
+
try {
|
|
1425
|
+
const res = await goosedumpMemoryStatus(signal, nativeExecutablePath);
|
|
1426
|
+
return textResult(res);
|
|
1427
|
+
} catch (err) {
|
|
1428
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1429
|
+
return textResult(`goosedump error: ${message}`);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}));
|
|
1189
1433
|
pi.registerTool(defineTool({
|
|
1190
1434
|
name: "goose_search",
|
|
1191
1435
|
label: "goose_search",
|
|
@@ -1208,7 +1452,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1208
1452
|
description: "Page number for ranked results"
|
|
1209
1453
|
}))
|
|
1210
1454
|
}),
|
|
1211
|
-
async execute(_id, params,
|
|
1455
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
1212
1456
|
if (!goosedumpAvailable)
|
|
1213
1457
|
return goosedumpUnavailableResult();
|
|
1214
1458
|
try {
|
|
@@ -1217,10 +1461,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1217
1461
|
if (!target)
|
|
1218
1462
|
return missingSessionId("goose_search", provider);
|
|
1219
1463
|
const page = params.page ?? 1;
|
|
1220
|
-
const messages = goosedumpSearch(target, params.query, {
|
|
1221
|
-
scope: params.scope ?? "lineage",
|
|
1222
|
-
page
|
|
1223
|
-
});
|
|
1464
|
+
const messages = await goosedumpSearch(target, params.query, { scope: params.scope ?? "lineage", page }, signal, nativeExecutablePath);
|
|
1224
1465
|
if (messages.length === 0) {
|
|
1225
1466
|
return textResult(`No results for "${params.query}"`);
|
|
1226
1467
|
}
|
|
@@ -1255,7 +1496,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1255
1496
|
session: historySessionParam(),
|
|
1256
1497
|
scope: historyScopeParam()
|
|
1257
1498
|
}),
|
|
1258
|
-
async execute(_id, params,
|
|
1499
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
1259
1500
|
if (!goosedumpAvailable)
|
|
1260
1501
|
return goosedumpUnavailableResult();
|
|
1261
1502
|
try {
|
|
@@ -1263,10 +1504,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1263
1504
|
const target = resolveTarget(ctx, provider, params.session);
|
|
1264
1505
|
if (!target)
|
|
1265
1506
|
return missingSessionId("goose_get", provider);
|
|
1266
|
-
const messages = goosedumpShow(target, {
|
|
1267
|
-
scope: params.scope ?? "lineage",
|
|
1268
|
-
ids: params.ids
|
|
1269
|
-
});
|
|
1507
|
+
const messages = await goosedumpShow(target, { scope: params.scope ?? "lineage", ids: params.ids }, signal, nativeExecutablePath);
|
|
1270
1508
|
if (messages.length === 0) {
|
|
1271
1509
|
return textResult("No entries found for the given IDs.");
|
|
1272
1510
|
}
|
|
@@ -1294,7 +1532,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1294
1532
|
session: historySessionParam(),
|
|
1295
1533
|
scope: historyScopeParam()
|
|
1296
1534
|
}),
|
|
1297
|
-
async execute(_id, params,
|
|
1535
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
1298
1536
|
if (!goosedumpAvailable)
|
|
1299
1537
|
return goosedumpUnavailableResult();
|
|
1300
1538
|
try {
|
|
@@ -1302,9 +1540,7 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1302
1540
|
const target = resolveTarget(ctx, provider, params.session);
|
|
1303
1541
|
if (!target)
|
|
1304
1542
|
return missingSessionId("goose_grep", provider);
|
|
1305
|
-
const messages = goosedumpGrep(target, params.pattern, {
|
|
1306
|
-
scope: params.scope ?? "lineage"
|
|
1307
|
-
});
|
|
1543
|
+
const messages = await goosedumpGrep(target, params.pattern, { scope: params.scope ?? "lineage" }, signal, nativeExecutablePath);
|
|
1308
1544
|
if (messages.length === 0) {
|
|
1309
1545
|
return textResult(`No matches for "${params.pattern}"`);
|
|
1310
1546
|
}
|
|
@@ -1344,6 +1580,72 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1344
1580
|
return textResult(compactStartedMessage(keep));
|
|
1345
1581
|
}
|
|
1346
1582
|
}));
|
|
1583
|
+
pi.registerTool(defineTool({
|
|
1584
|
+
name: "goose_sessions",
|
|
1585
|
+
label: "goose_sessions",
|
|
1586
|
+
description: "List or delete session IDs for the current working directory.",
|
|
1587
|
+
promptSnippet: "List or delete coding agent sessions for current directory",
|
|
1588
|
+
promptGuidelines: [
|
|
1589
|
+
"Use goose_sessions to discover sessions in the current directory or delete a non-active session.",
|
|
1590
|
+
"Strictly scoped to the current working directory.",
|
|
1591
|
+
"Deleting the currently active session is not allowed."
|
|
1592
|
+
],
|
|
1593
|
+
parameters: Type.Object({
|
|
1594
|
+
action: Type.Optional(Type.Union([Type.Literal("list"), Type.Literal("delete")], {
|
|
1595
|
+
default: "list",
|
|
1596
|
+
description: "Action: list (default) or delete"
|
|
1597
|
+
})),
|
|
1598
|
+
provider: historyProviderParam("list"),
|
|
1599
|
+
session: Type.Optional(Type.String({
|
|
1600
|
+
description: "Session ID to delete (required for action: delete)"
|
|
1601
|
+
}))
|
|
1602
|
+
}),
|
|
1603
|
+
async execute(_id, params, signal, _onUpdate, ctx) {
|
|
1604
|
+
if (!goosedumpAvailable)
|
|
1605
|
+
return goosedumpUnavailableResult();
|
|
1606
|
+
const action = params.action ?? "list";
|
|
1607
|
+
const provider = params.provider ?? "pi";
|
|
1608
|
+
const currentTarget = currentSessionTarget(ctx);
|
|
1609
|
+
if (action === "delete") {
|
|
1610
|
+
if (!params.session) {
|
|
1611
|
+
return textResult("goose_sessions error: session ID is required for action: delete");
|
|
1612
|
+
}
|
|
1613
|
+
const target = formatTarget(provider, params.session);
|
|
1614
|
+
if (currentTarget && target === currentTarget) {
|
|
1615
|
+
return textResult("Cannot delete the active session.");
|
|
1616
|
+
}
|
|
1617
|
+
try {
|
|
1618
|
+
await goosedumpRemoveSession(target, signal, nativeExecutablePath);
|
|
1619
|
+
return textResult(`Deleted session ${target}`);
|
|
1620
|
+
} catch (err) {
|
|
1621
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1622
|
+
return textResult(`goosedump error: ${message}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
try {
|
|
1626
|
+
const sessions = await goosedumpListSessions(signal, nativeExecutablePath);
|
|
1627
|
+
const cwdSessions = sessions.filter((s) => s.cwd === ctx.cwd && s.provider === provider);
|
|
1628
|
+
if (cwdSessions.length === 0) {
|
|
1629
|
+
return textResult(`No ${provider} sessions found for ${ctx.cwd}`);
|
|
1630
|
+
}
|
|
1631
|
+
const lines = cwdSessions.map((s) => {
|
|
1632
|
+
const target = formatTarget(s.provider, s.id);
|
|
1633
|
+
const isActive = currentTarget === target;
|
|
1634
|
+
const activeMarker = isActive ? " (active)" : "";
|
|
1635
|
+
const dateStr = s.from ? new Date(s.from).toISOString() : "unknown date";
|
|
1636
|
+
const countStr = `${s.count} entries`;
|
|
1637
|
+
return `- ${s.id}${activeMarker} [${countStr}; ${dateStr}]`;
|
|
1638
|
+
});
|
|
1639
|
+
return textResult(`Sessions for ${ctx.cwd} (${provider}):
|
|
1640
|
+
|
|
1641
|
+
${lines.join(`
|
|
1642
|
+
`)}`);
|
|
1643
|
+
} catch (err) {
|
|
1644
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1645
|
+
return textResult(`goosedump error: ${message}`);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}));
|
|
1347
1649
|
}
|
|
1348
1650
|
function maybeAutoCompact(ctx, resumeAfter) {
|
|
1349
1651
|
if (!goosedumpAvailable || !shouldOverrideDefaultCompaction)
|
|
@@ -1525,6 +1827,60 @@ function createGoosedumpIntegration(options = {}) {
|
|
|
1525
1827
|
}
|
|
1526
1828
|
}
|
|
1527
1829
|
});
|
|
1830
|
+
maybePi.registerCommand?.("goose-sessions", {
|
|
1831
|
+
description: "Manage and switch between sessions for the current working directory",
|
|
1832
|
+
handler: async (_args, ctx) => {
|
|
1833
|
+
if (!goosedumpAvailable) {
|
|
1834
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, "error");
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
try {
|
|
1838
|
+
await runSessionsPanel(ctx, nativeExecutablePath);
|
|
1839
|
+
} catch (err) {
|
|
1840
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1841
|
+
ctx.ui.notify(`goosedump error: ${message}`, "error");
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
maybePi.registerCommand?.("goose-forget", {
|
|
1846
|
+
description: "Forget/remove an entry from goosedump durable memory",
|
|
1847
|
+
handler: async (args, ctx) => {
|
|
1848
|
+
if (!goosedumpAvailable) {
|
|
1849
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, "error");
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
let target = typeof args === "string" ? args.trim() : "";
|
|
1853
|
+
if (target.length === 0) {
|
|
1854
|
+
const input = await ctx.ui.input("Memory target/ID to forget:", "");
|
|
1855
|
+
target = (input ?? "").trim();
|
|
1856
|
+
}
|
|
1857
|
+
if (target.length === 0)
|
|
1858
|
+
return;
|
|
1859
|
+
try {
|
|
1860
|
+
const res = await goosedumpMemoryForget(target, undefined, nativeExecutablePath);
|
|
1861
|
+
ctx.ui.notify(res, "info");
|
|
1862
|
+
} catch (err) {
|
|
1863
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1864
|
+
ctx.ui.notify(`goosedump error: ${message}`, "error");
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
maybePi.registerCommand?.("goose-memory-status", {
|
|
1869
|
+
description: "Show durable memory status and health stats",
|
|
1870
|
+
handler: async (_args, ctx) => {
|
|
1871
|
+
if (!goosedumpAvailable) {
|
|
1872
|
+
ctx.ui.notify(GOOSEDUMP_UNAVAILABLE_MESSAGE, "error");
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
try {
|
|
1876
|
+
const res = await goosedumpMemoryStatus(undefined, nativeExecutablePath);
|
|
1877
|
+
ctx.ui.notify(res, "info");
|
|
1878
|
+
} catch (err) {
|
|
1879
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1880
|
+
ctx.ui.notify(`goosedump error: ${message}`, "error");
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1528
1884
|
}
|
|
1529
1885
|
return { register };
|
|
1530
1886
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-goosedump",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Pi extension for goosedump-backed session-history search, entry expansion, and compaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compaction",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@earendil-works/pi-tui": "^0.78.0",
|
|
41
|
-
"@jarkkojs/goosedump": "^0.12.
|
|
41
|
+
"@jarkkojs/goosedump": "^0.12.1",
|
|
42
42
|
"@sinclair/typebox": "^0.34.49"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|