pi-goosedump 0.10.4 → 0.10.5

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.
Files changed (3) hide show
  1. package/README.md +54 -74
  2. package/index.ts +30 -14
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,69 +1,62 @@
1
1
  # pi-goosedump
2
2
 
3
- `pi-goosedump` is a pi extension for goosedump-backed session-history search,
4
- entry expansion, and compaction. It exposes goosedump tools under the `goose_`
5
- prefix and replaces Pi's default LLM compaction with goosedump-generated
6
- summaries.
3
+ `pi-goosedump` adds goosedump-backed session-history search, durable-memory
4
+ recall, entry expansion, and compaction to Pi. Its tools use the `goose_`
5
+ prefix.
7
6
 
8
- ## Installation
7
+ ## Install
9
8
 
10
- ```bash
9
+ ```sh
11
10
  pi install npm:pi-goosedump
12
11
  ```
13
12
 
14
- This installs `pi-goosedump` and its main dependency `@jarkkojs/goosedump`,
15
- which includes platform-specific native binaries for Linux x64, macOS arm64,
16
- and Windows x64.
17
- If the goosedump binary cannot be resolved, the extension disables itself.
13
+ The package installs `@jarkkojs/goosedump`, including native binaries for Linux
14
+ x64, macOS arm64, and Windows x64. If no compatible binary is available, the
15
+ extension keeps Pi's normal compaction behavior and its goosedump features
16
+ report that they are unavailable.
18
17
 
19
18
  ## Tools
20
19
 
21
- - **goose_search:** ranks messages by query relevance; returns compact entry
22
- overviews with IDs.
23
- - **goose_recall:** searches durable memory harvested from earlier compactions,
24
- scoped to the current working directory by default.
25
- - **goose_get:** expands specific entry IDs to their full content.
26
- - **goose_grep:** filters messages by glob pattern; returns compact entries
27
- with IDs.
28
- - **goose_compact:** triggers a goosedump compaction now, optionally keeping
29
- the last N user turns verbatim while summarizing everything before.
30
-
31
- The read tools (`goose_search`, `goose_get`, `goose_grep`) each accept an
32
- optional `provider` (default `pi`; also `claude`, `codex`, `crush`, `gemini`,
33
- `goose`, `opencode`) and `session` id. `session` defaults to the current Pi
34
- session for `pi` and is required otherwise. `scope` is `lineage` (default,
35
- current branch) or `all`. Targets are always provider-qualified
36
- (`provider:id`); a bare id is never sent to goosedump.
20
+ - **`goose_search`** ranks session messages and returns compact overviews with
21
+ entry IDs.
22
+ - **`goose_get`** expands entry IDs returned by `goose_search` or `goose_grep`.
23
+ - **`goose_grep`** filters session messages with a glob pattern.
24
+ - **`goose_recall`** searches memory harvested by earlier compactions. It
25
+ searches the current working directory by default and returns full entries
26
+ with provenance.
27
+ - **`goose_compact`** starts asynchronous compaction of the current Pi session.
28
+ Set `keep` to preserve the last N user turns verbatim.
37
29
 
38
- ```
30
+ The session-history tools accept an optional `provider` (`pi` by default),
31
+ `session`, and `scope` (`lineage` by default or `all`). The current Pi session
32
+ is used when both provider and session are omitted. Non-Pi providers require an
33
+ explicit session ID. Targets sent to goosedump are always provider-qualified.
34
+
35
+ Examples:
36
+
37
+ ```text
39
38
  goose_search({ query: "bug fix" })
40
- goose_recall({ query: "prior architecture decision" })
41
39
  goose_search({ query: "auth", scope: "all", page: 2 })
42
40
  goose_get({ ids: ["entry-a", "entry-b"] })
43
- goose_get({ ids: ["entry-a"], provider: "claude", session: "1f6b1eba-…" })
44
- goose_grep({ pattern: "*rand*" })
41
+ goose_grep({ pattern: "*timeout*" })
42
+ goose_recall({ query: "prior architecture decision" })
45
43
  goose_compact({ keep: 2 })
46
44
  ```
47
45
 
48
46
  ## Commands
49
47
 
50
- - **`/goose-search [query]`:** search the current session history; accepts a
51
- query argument or prompts inline when bare. Shows ranked results in a
52
- scrollable panel; press enter to expand an entry to its full content.
53
- - **`/goose-compact [keep:N]`:** compact now with goosedump. With `keep:N`
54
- (N ≥ 1) the last N user turns stay verbatim and everything before is
55
- summarized; with no argument goosedump summarizes up to Pi's token-based cut.
56
- - **`/goose-settings`:** edit the global and project compaction settings.
48
+ - **`/goose-search [query]`** searches the current session. A bare command opens
49
+ an inline prompt; selecting a result expands it.
50
+ - **`/goose-compact [keep:N]`** compacts now. With `keep:N`, the last N user
51
+ turns remain verbatim; without it, Pi chooses the cut point.
52
+ - **`/goose-settings`** edits global and project compaction settings.
57
53
 
58
54
  ## Settings
59
55
 
60
- `pi-goosedump` reads optional JSON settings from:
56
+ Optional settings live under `goosedump` in:
61
57
 
62
- - `~/.pi/agent/settings.json` — Global
63
- - `.pi/settings.json` — Project
64
-
65
- Project settings override global settings. All settings live in a `goosedump`
66
- section and are optional (defaults shown):
58
+ - `~/.pi/agent/settings.json` for global settings.
59
+ - `.pi/settings.json` for project overrides.
67
60
 
68
61
  ```json
69
62
  {
@@ -73,37 +66,24 @@ section and are optional (defaults shown):
73
66
  }
74
67
  ```
75
68
 
76
- `turnsBeforeCompact` sets how many LLM turns elapse before goosedump triggers
77
- compaction, as an integer 0–255. `0` disables the counter. The settings editor
78
- can clear the project value to inherit Global again, and writes updates atomically.
79
- The countdown is reconstructed from the active session after Pi restarts or
80
- resumes, rather than restarting at the configured value. When enabled, the footer
81
- shows `GD: XX` counting down in hex (e.g. `GD: 0A`); `GD: 00` is armed and waiting
82
- to compact, while `GD: --` means disabled.
83
-
84
- ### Compaction flow
85
-
86
- pi-goosedump hooks Pi's `session_before_compact` event — fired by manual
87
- `/compact`, the turn counter above, and Pi's own auto-compaction — and
88
- substitutes a goosedump summary for Pi's LLM summary. It resolves the current
89
- session to its goosedump context by reading the `id` field from the session
90
- header line, then runs `goosedump compact` over the replacement range:
91
-
92
- - `--from` is the entry following the most recent prior compaction (so the
93
- summary starts where the previous one left off); omitted on the first
94
- compaction.
95
- - `--until` is the first entry Pi is keeping (`firstKeptEntryId`).
96
-
97
- goosedump composes the structured summary itself and emits a native Pi
98
- compaction entry, from which the extension extracts the `summary` field. The
99
- extension prepends Pi's previous compaction summary when present, so prior goals,
100
- progress, and context survive incremental compactions. A `keep:N` override (via
101
- `/goose-compact` or the `goose_compact` tool) replaces Pi's token-based cut with
102
- a turn-based one: summarize everything before the Nth-from-last user entry in the
103
- active, uncompacted history and keep the tail verbatim. Fewer than N active user
104
- turns cancels with a notice. On completion Pi notifies the token reduction.
69
+ `turnsBeforeCompact` is an integer from 0 to 255. `0` disables scheduled
70
+ compaction. The countdown is restored when a session resumes and appears in the
71
+ footer as hexadecimal: `GD: 0A` means ten turns remain, `GD: 00` is ready to
72
+ compact, and `GD: --` is disabled. Clearing the project value restores the
73
+ global setting.
74
+
75
+ ## Compaction behavior
76
+
77
+ The extension replaces Pi's normal summary during manual, scheduled, and
78
+ automatic compaction. It preserves the previous compaction summary and asks
79
+ goosedump to summarize only the newly compacted range. A `keep:N` request
80
+ instead keeps the last N active user turns; it is cancelled when no earlier
81
+ history is available to summarize.
82
+
83
+ Compactions with custom instructions continue to use Pi's normal compactor. If
84
+ goosedump fails, the extension reports the error and falls back to Pi. Successful
85
+ compactions report memory-learning warnings without discarding the summary.
105
86
 
106
87
  ## License
107
88
 
108
- `pi-goosedump` is licensed under `Apache-2.0`. See [LICENSE](LICENSE) for more
109
- information.
89
+ `pi-goosedump` is licensed under `Apache-2.0`. See [LICENSE](LICENSE).
package/index.ts CHANGED
@@ -58,6 +58,11 @@ interface GoosedumpMessage {
58
58
  content: string;
59
59
  }
60
60
 
61
+ interface GoosedumpCommandOutput {
62
+ stdout: string;
63
+ stderr: string;
64
+ }
65
+
61
66
  interface GoosedumpMemoryHit {
62
67
  hash: string;
63
68
  kind: string;
@@ -67,6 +72,7 @@ interface GoosedumpMemoryHit {
67
72
  context_id: string;
68
73
  entry_id: string;
69
74
  path: string;
75
+ source_path: string;
70
76
  }
71
77
 
72
78
  // A `content` part of a pi-native message line.
@@ -324,7 +330,8 @@ function parseMemoryHits(output: string): GoosedumpMemoryHit[] {
324
330
  typeof item.provider === 'string' &&
325
331
  typeof item.context_id === 'string' &&
326
332
  typeof item.entry_id === 'string' &&
327
- typeof item.path === 'string',
333
+ typeof item.path === 'string' &&
334
+ typeof item.source_path === 'string',
328
335
  );
329
336
  }
330
337
 
@@ -332,7 +339,7 @@ function formatMemoryHits(hits: GoosedumpMemoryHit[]): string {
332
339
  return hits
333
340
  .map(
334
341
  (hit) =>
335
- `[${hit.hash}] ${hit.kind} (${hit.provider}:${hit.context_id}/${hit.entry_id})\n${hit.text}`,
342
+ `[${hit.hash}] ${hit.kind} (${hit.provider}:${hit.context_id}/${hit.entry_id})\nWorking directory: ${hit.path}\nSource: ${hit.source_path}\n${hit.text}`,
336
343
  )
337
344
  .join('\n\n---\n\n');
338
345
  }
@@ -394,7 +401,7 @@ function runGoosedump(args: string[]): string {
394
401
  // Non-blocking variant for the compact path: goosedump compact runs an ML
395
402
  // model and can take many seconds, so execFileSync would freeze Pi's event
396
403
  // loop (and its compaction spinner). execFile keeps the loop free to render.
397
- function runGoosedumpAsync(args: string[], signal?: AbortSignal): Promise<string> {
404
+ function runGoosedumpAsync(args: string[], signal?: AbortSignal): Promise<GoosedumpCommandOutput> {
398
405
  return new Promise((resolve, reject) => {
399
406
  execFile(
400
407
  process.execPath,
@@ -409,7 +416,7 @@ function runGoosedumpAsync(args: string[], signal?: AbortSignal): Promise<string
409
416
  const message = stderr.trim() || err.message;
410
417
  reject(new Error(message));
411
418
  } else {
412
- resolve(stdout);
419
+ resolve({ stdout, stderr });
413
420
  }
414
421
  },
415
422
  );
@@ -475,12 +482,12 @@ async function goosedumpRecall(
475
482
  query: string,
476
483
  options: { provider?: string; path?: string; limit?: number } = {},
477
484
  signal?: AbortSignal,
478
- ): Promise<GoosedumpMemoryHit[]> {
485
+ ): Promise<{ hits: GoosedumpMemoryHit[]; warning: string }> {
479
486
  const args = ['recall', query, '--limit', String(options.limit ?? 5)];
480
487
  if (options.provider) args.push('--provider', options.provider);
481
488
  if (options.path) args.push('--path', options.path);
482
489
  const output = await runGoosedumpAsync(args, signal);
483
- return parseMemoryHits(output);
490
+ return { hits: parseMemoryHits(output.stdout), warning: output.stderr.trim() };
484
491
  }
485
492
 
486
493
  // Resolve a goosedump 0.9 `provider:id` target. An explicit contextId is the
@@ -537,23 +544,25 @@ async function goosedumpCompact(
537
544
  target: string,
538
545
  options: { scope?: string; from?: string; until: string },
539
546
  signal?: AbortSignal,
540
- ): Promise<string> {
547
+ ): Promise<{ summary: string; warning: string }> {
541
548
  const scope = options.scope ?? 'lineage';
542
549
  const args = ['compact', target, '--scope', scope];
543
550
  if (options.from) args.push('--from', options.from);
544
551
  args.push('--until', options.until);
545
552
 
546
553
  const output = await runGoosedumpAsync(args, signal);
547
- for (const line of output.split('\n')) {
554
+ for (const line of output.stdout.split('\n')) {
548
555
  if (!line) continue;
549
556
  try {
550
557
  const entry = JSON.parse(line) as PiEntryJson;
551
- if (entry.type === 'compaction' && typeof entry.summary === 'string') return entry.summary;
558
+ if (entry.type === 'compaction' && typeof entry.summary === 'string') {
559
+ return { summary: entry.summary, warning: output.stderr.trim() };
560
+ }
552
561
  } catch {
553
562
  continue;
554
563
  }
555
564
  }
556
- return output.trim();
565
+ return { summary: output.stdout.trim(), warning: output.stderr.trim() };
557
566
  }
558
567
 
559
568
  export interface GoosedumpIntegrationOptions {
@@ -574,6 +583,7 @@ interface GoosedumpCompactionDetails {
574
583
  from?: string;
575
584
  until: string;
576
585
  fileOps: unknown;
586
+ warning?: string;
577
587
  }
578
588
 
579
589
  export interface GoosedumpIntegration {
@@ -789,9 +799,9 @@ async function buildGoosedumpCompaction(
789
799
  const range = buildCompactRange(branchEntries, firstKeptEntryId);
790
800
  if (!range) return undefined;
791
801
 
792
- const generatedSummary = await goosedumpCompact(target, range, signal);
793
- if (!generatedSummary) return undefined;
794
- const summary = mergeCompactionSummary(preparation.previousSummary, generatedSummary);
802
+ const generated = await goosedumpCompact(target, range, signal);
803
+ if (!generated.summary) return undefined;
804
+ const summary = mergeCompactionSummary(preparation.previousSummary, generated.summary);
795
805
 
796
806
  return {
797
807
  summary,
@@ -804,6 +814,7 @@ async function buildGoosedumpCompaction(
804
814
  from: range.from,
805
815
  until: range.until,
806
816
  fileOps: preparation.fileOps,
817
+ warning: generated.warning || undefined,
807
818
  },
808
819
  };
809
820
  }
@@ -1390,7 +1401,7 @@ export function createGoosedumpIntegration(
1390
1401
  async execute(_id, params, signal, _onUpdate, ctx): Promise<AgentToolResult<void>> {
1391
1402
  if (!goosedumpAvailable) return goosedumpUnavailableResult();
1392
1403
  try {
1393
- const hits = await goosedumpRecall(
1404
+ const { hits, warning } = await goosedumpRecall(
1394
1405
  params.query,
1395
1406
  {
1396
1407
  provider: params.provider,
@@ -1399,6 +1410,7 @@ export function createGoosedumpIntegration(
1399
1410
  },
1400
1411
  signal,
1401
1412
  );
1413
+ if (warning && ctx.hasUI) ctx.ui.notify(warning, 'warning');
1402
1414
  if (hits.length === 0) return textResult(`No durable memory for "${params.query}"`);
1403
1415
  return textResult(formatMemoryHits(hits));
1404
1416
  } catch (err) {
@@ -1680,6 +1692,10 @@ export function createGoosedumpIntegration(
1680
1692
  if (ctx.hasUI) ctx.ui.notify(compaction.reason, 'info');
1681
1693
  return { cancel: true };
1682
1694
  }
1695
+ const warning = compaction?.details?.warning;
1696
+ if (warning && ctx.hasUI) {
1697
+ ctx.ui.notify(warning, 'warning');
1698
+ }
1683
1699
  return compaction ? { compaction } : undefined;
1684
1700
  } catch (err) {
1685
1701
  if (event.signal.aborted) return { cancel: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goosedump",
3
- "version": "0.10.4",
3
+ "version": "0.10.5",
4
4
  "description": "Pi extension for goosedump-backed session-history search, entry expansion, and compaction",
5
5
  "keywords": [
6
6
  "compaction",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@earendil-works/pi-tui": "^0.78.0",
36
- "@jarkkojs/goosedump": "^0.10.4",
36
+ "@jarkkojs/goosedump": "^0.10.5",
37
37
  "@sinclair/typebox": "^0.34.49"
38
38
  },
39
39
  "devDependencies": {