pi-goosedump 0.13.7 → 0.13.8

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 (2) hide show
  1. package/dist/index.ts +67 -18
  2. package/package.json +2 -2
package/dist/index.ts CHANGED
@@ -725,6 +725,7 @@ import {
725
725
  mkdirSync,
726
726
  readFileSync as readFileSync3,
727
727
  renameSync,
728
+ statSync as statSync2,
728
729
  unlinkSync,
729
730
  writeFileSync
730
731
  } from "node:fs";
@@ -851,7 +852,26 @@ function validateGoosedumpSettings(path) {
851
852
  }
852
853
  var isNumberValue = (value) => typeof value === "number";
853
854
  var isBooleanValue = (value) => typeof value === "boolean";
855
+ var settingsCache = null;
856
+ function settingsStamp(path) {
857
+ try {
858
+ const stat = statSync2(path);
859
+ return `${stat.mtimeMs}:${stat.size}:${stat.ino}`;
860
+ } catch {
861
+ return "missing";
862
+ }
863
+ }
854
864
  function resolveWorkspaceSettings(cwd, includeProject) {
865
+ const globalPath = globalSettingsPath();
866
+ const projectPath = includeProject ? projectSettingsPath(cwd) : undefined;
867
+ const key = `${globalPath}|${settingsStamp(globalPath)}|${projectPath ?? ""}|${projectPath ? settingsStamp(projectPath) : ""}`;
868
+ if (settingsCache?.key === key)
869
+ return settingsCache.value;
870
+ const value = computeWorkspaceSettings(cwd, includeProject);
871
+ settingsCache = { key, value };
872
+ return value;
873
+ }
874
+ function computeWorkspaceSettings(cwd, includeProject) {
855
875
  const globalObj = readJsonObject(globalSettingsPath());
856
876
  const projectObj = includeProject ? readJsonObject(projectSettingsPath(cwd)) : undefined;
857
877
  const goosedump = {
@@ -905,6 +925,7 @@ function updateScopedGoosedumpSetting(cwd, scope, update) {
905
925
  delete root.goosedump;
906
926
  else
907
927
  root.goosedump = next;
928
+ settingsCache = null;
908
929
  writeJsonObjectAtomically(path, root);
909
930
  }
910
931
  function readScopedGoosedumpSettings(cwd, scope) {
@@ -983,12 +1004,22 @@ function latestCompaction(branchEntries) {
983
1004
  }
984
1005
  return;
985
1006
  }
1007
+
1008
+ class BranchPositions {
1009
+ positions = new Map;
1010
+ constructor(branchEntries) {
1011
+ branchEntries.forEach((entry, index) => this.positions.set(entry.id, index));
1012
+ }
1013
+ boundaryStartIndex(compaction) {
1014
+ const kept = this.positions.get(compaction.firstKeptEntryId);
1015
+ if (kept !== undefined)
1016
+ return kept;
1017
+ const own = this.positions.get(compaction.id);
1018
+ return own !== undefined ? own + 1 : 0;
1019
+ }
1020
+ }
986
1021
  function compactionBoundaryStartIndex(branchEntries, compaction) {
987
- const keptIndex = branchEntries.findIndex((entry) => entry.id === compaction.firstKeptEntryId);
988
- if (keptIndex >= 0)
989
- return keptIndex;
990
- const compactionIndex = branchEntries.findIndex((entry) => entry.id === compaction.id);
991
- return compactionIndex >= 0 ? compactionIndex + 1 : 0;
1022
+ return new BranchPositions(branchEntries).boundaryStartIndex(compaction);
992
1023
  }
993
1024
  function activeHistoryStartIndex(branchEntries) {
994
1025
  const compaction = latestCompaction(branchEntries);
@@ -1022,12 +1053,13 @@ function buildCompactRange(branchEntries, firstKeptEntryId) {
1022
1053
  const untilIndex = branchEntries.findIndex((entry) => entry.id === firstKeptEntryId);
1023
1054
  if (untilIndex < 0)
1024
1055
  return;
1056
+ const positions = new BranchPositions(branchEntries);
1025
1057
  let from;
1026
1058
  for (let i = Math.min(untilIndex - 1, branchEntries.length - 1);i >= 0; i -= 1) {
1027
1059
  const entry = branchEntries[i];
1028
1060
  if (!isCompactionEntry(entry))
1029
1061
  continue;
1030
- const boundaryStart = compactionBoundaryStartIndex(branchEntries, entry);
1062
+ const boundaryStart = positions.boundaryStartIndex(entry);
1031
1063
  if (boundaryStart < untilIndex) {
1032
1064
  from = branchEntries[boundaryStart]?.id;
1033
1065
  break;
@@ -2406,6 +2438,7 @@ ${formattedContent}` : summary;
2406
2438
  var renderGooseSessionsResult = renderGooseDumpSessionsResult;
2407
2439
 
2408
2440
  // src/tools.ts
2441
+ var MAX_ENTRY_IDS = 50;
2409
2442
  function memoryDisabledResult() {
2410
2443
  return textResult(MEMORY_DISABLED_MESSAGE);
2411
2444
  }
@@ -2600,6 +2633,7 @@ ${text}`);
2600
2633
  registerPair("goosedump_get", "goose_get", Type3.Object({
2601
2634
  ids: Type3.Array(Type3.String(), {
2602
2635
  minItems: 1,
2636
+ maxItems: MAX_ENTRY_IDS,
2603
2637
  description: PARAM_DESCRIPTIONS.entryIds
2604
2638
  }),
2605
2639
  ...historyBranchParams()
@@ -3025,7 +3059,7 @@ async function runSettingsEditor(ctx) {
3025
3059
  async function runSearchPanel(ctx, query, executablePath) {
3026
3060
  const OVERLAY_WIDTH = 84;
3027
3061
  const VIEWPORT = 18;
3028
- const PAGE_SIZE = 5;
3062
+ const MAX_PAGES = 10;
3029
3063
  const wrapWidth = OVERLAY_WIDTH - 4;
3030
3064
  await ctx.ui.custom((tui, theme, _kb, done) => {
3031
3065
  const target = currentSessionTarget(ctx);
@@ -3038,9 +3072,11 @@ async function runSearchPanel(ctx, query, executablePath) {
3038
3072
  let detailLines = [];
3039
3073
  let detailScroll = 0;
3040
3074
  let detailEntry = null;
3075
+ let truncated = false;
3076
+ const controller = new AbortController;
3041
3077
  async function runSearch() {
3042
3078
  try {
3043
- const first = await goosedumpSearch(target, query, { scope: "lineage", page: 1 }, undefined, executablePath);
3079
+ const first = await goosedumpSearch(target, query, { scope: "lineage", page: 1 }, controller.signal, executablePath);
3044
3080
  if (first.length === 0) {
3045
3081
  entries = [];
3046
3082
  error = `No results for "${query}"`;
@@ -3048,22 +3084,34 @@ async function runSearchPanel(ctx, query, executablePath) {
3048
3084
  scroll = 0;
3049
3085
  return;
3050
3086
  }
3087
+ const search = (page) => goosedumpSearch(target, query, { scope: "lineage", page }, controller.signal, executablePath);
3051
3088
  const all = [...first];
3052
- for (let page = 2;page <= 10; page += 1) {
3053
- const result = await goosedumpSearch(target, query, { scope: "lineage", page }, undefined, executablePath);
3054
- if (result.length === 0)
3055
- break;
3056
- all.push(...result);
3057
- if (result.length < PAGE_SIZE)
3058
- break;
3089
+ let lastPage = 1;
3090
+ let complete = false;
3091
+ const second = await search(2);
3092
+ if (second.length < first.length) {
3093
+ all.push(...second);
3094
+ complete = true;
3095
+ } else {
3096
+ all.push(...second);
3097
+ const rest = await Promise.all(Array.from({ length: MAX_PAGES - 2 }, (_unused, index) => search(index + 3)));
3098
+ for (const [index, page] of rest.entries()) {
3099
+ all.push(...page);
3100
+ lastPage = index + 3;
3101
+ if (page.length < first.length) {
3102
+ complete = true;
3103
+ break;
3104
+ }
3105
+ }
3059
3106
  }
3107
+ truncated = !complete && lastPage >= MAX_PAGES;
3060
3108
  entries = all;
3061
3109
  error = null;
3062
3110
  selected = 0;
3063
3111
  scroll = 0;
3064
3112
  } catch (err) {
3065
3113
  entries = [];
3066
- error = err instanceof Error ? err.message : String(err);
3114
+ error = controller.signal.aborted ? "Search cancelled." : err instanceof Error ? err.message : String(err);
3067
3115
  } finally {
3068
3116
  tui.requestRender();
3069
3117
  }
@@ -3075,7 +3123,7 @@ async function runSearchPanel(ctx, query, executablePath) {
3075
3123
  }
3076
3124
  async function loadDetail(id) {
3077
3125
  try {
3078
- const messages = await goosedumpShow(target, { ids: [id] }, undefined, executablePath);
3126
+ const messages = await goosedumpShow(target, { ids: [id] }, controller.signal, executablePath);
3079
3127
  const msg = messages.length > 0 ? messages[0] : null;
3080
3128
  detailEntry = msg;
3081
3129
  const entryHeader = msg ? `${colors.accent(colors.bold(`[${msg.id}]`))} ${colors.muted(`${msg.role}:`)}` : "";
@@ -3106,7 +3154,7 @@ async function runSearchPanel(ctx, query, executablePath) {
3106
3154
  }
3107
3155
  return [
3108
3156
  header,
3109
- ...overlayViewport(colors, width, entries, scroll, VIEWPORT, "↑↓ navigate enter expand esc close", (count) => `entr${count === 1 ? "y" : "ies"}`, (entry, absolute) => {
3157
+ ...overlayViewport(colors, width, entries, scroll, VIEWPORT, truncated ? `first ${entries.length} results ↑↓ navigate enter expand esc close` : "↑↓ navigate enter expand esc close", (count) => `entr${count === 1 ? "y" : "ies"}`, (entry, absolute) => {
3110
3158
  const isSelected = absolute === selected;
3111
3159
  const cursor = isSelected ? colors.accent(colors.bold("▶")) : " ";
3112
3160
  const idText = isSelected ? colors.accent(colors.bold(entry.id)) : colors.text(entry.id);
@@ -3123,6 +3171,7 @@ async function runSearchPanel(ctx, query, executablePath) {
3123
3171
  tui.requestRender();
3124
3172
  return;
3125
3173
  }
3174
+ controller.abort();
3126
3175
  done(undefined);
3127
3176
  return;
3128
3177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-goosedump",
3
- "version": "0.13.7",
3
+ "version": "0.13.8",
4
4
  "description": "Pi extension for goosedump session search, durable memory, session management, and compaction",
5
5
  "keywords": [
6
6
  "compaction",
@@ -37,7 +37,7 @@
37
37
  "ci:test": "npm test"
38
38
  },
39
39
  "dependencies": {
40
- "@jarkkojs/goosedump": "^0.13.7"
40
+ "@jarkkojs/goosedump": "^0.13.8"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@earendil-works/pi-coding-agent": "^0.84.2",