@howaboua/pi-notes-compaction 0.0.1 → 0.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @howaboua/pi-notes-compaction
2
2
 
3
+ ## 0.0.2
4
+
5
+ - Added a `/notes` settings panel for normal compaction, idle rollover, continuity status and confirmed pruning, replacing the subcommands and JSON status output.
6
+
3
7
  ## 0.0.1
4
8
 
5
9
  - Added Notes Compaction: local checkpoint notes, searchable session history and notes-guided context windows for any Pi model, with optional Codex Conversion integration and cached remote lookup.
package/README.md CHANGED
@@ -12,7 +12,7 @@ pi install npm:@howaboua/pi-notes-compaction
12
12
 
13
13
  Reload Pi, then ask the agent to save a checkpoint and start a new context window. It can read those notes and search earlier session history to continue.
14
14
 
15
- With Pi Codex Conversion 3.0.48 or newer, local notes work alongside Codex tools, Code Mode, Notebook and normal compaction. Older PCC versions without this integration leave the extension inactive. Run `/notes status` to check activation and available capabilities. Extension authors can read the [integration contract](PCC-INTEGRATION.md).
15
+ With Pi Codex Conversion 3.0.48 or newer, local notes work alongside Codex tools, Code Mode, Notebook and normal compaction. Older PCC versions without this integration leave the extension inactive. Run `/notes` to check activation and available capabilities. Extension authors can read the [integration contract](PCC-INTEGRATION.md).
16
16
 
17
17
  ## Notes and windows
18
18
 
@@ -22,15 +22,15 @@ By default, `new_context` starts a fresh window without generating a summary. Th
22
22
 
23
23
  Reminders arrive at 85% and 90% context use. `/compact` requests a notes checkpoint and opens a new window only after a completed run with fresh saved notes. Abort, errors and failed writes retain the old context. Overflow recovery still uses Pi's normal compaction.
24
24
 
25
- Idle rollover is off by default. Enable it with `/notes idle 5`, `15`, `25` or `55` (minutes). After that interval, the next input waits for rollover only if the latest completed turn saved fresh notes. Otherwise it proceeds in the current window without requesting a checkpoint. These intervals are user preferences, not provider cache-expiry guarantees. Held input retains its original text, attachments and SDK options. A failed rollover keeps input pending. Submit another prompt to retry, or reload to cancel it.
25
+ Idle rollover is off by default. Choose 5, 15, 25 or 55 minutes in `/notes`. After that interval, the next input waits for rollover only if the latest completed turn saved fresh notes. Otherwise it proceeds in the current window without requesting a checkpoint. These intervals are user preferences, not provider cache-expiry guarantees. Held input retains its original text, attachments and SDK options. A failed rollover keeps input pending. Submit another prompt to retry, or reload to cancel it.
26
26
 
27
27
  ## Management
28
28
 
29
- - `/notes status` shows activation, storage counts and optional remote capabilities.
30
- - `/notes compact on` runs the installed normal Pi or PCC compaction flow, including on `new_context`.
31
- - `/notes compact off` restores notes-only rollover. This is the default.
32
- - `/notes idle off` disables idle rollover. The interval is saved alongside the compaction setting.
33
- - `/notes prune` removes SQLite records for sessions whose recorded files are missing. Unknown paths, permission errors and the active session are retained.
29
+ `/notes` opens the settings panel in interactive Pi. It shows continuity status, storage counts and remote capabilities. Changes save immediately.
30
+
31
+ - **Normal compaction** includes the installed Pi or PCC compaction flow on `new_context`. Off keeps notes-only rollover, the default.
32
+ - **Idle rollover** selects the interval or disables it.
33
+ - **Prune missing sessions** removes records for missing session files after confirmation. Unknown paths, permission errors and the active session are retained.
34
34
 
35
35
  Notes are stored as plaintext in `<Pi agent directory>/notes-compaction/notes.sqlite`, honoring `PI_CODING_AGENT_DIR`. Saved session snapshots preserve notes across forks and branch navigation, including after database pruning. Existing plaintext PCC notes remain readable without an import.
36
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/pi-notes-compaction",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/index.ts CHANGED
@@ -12,7 +12,8 @@ import {
12
12
  } from "./bridge.js";
13
13
  import { NotesLifecycle } from "./lifecycle.js";
14
14
  import { exportNotesSnapshot, sessionRef } from "./notes.js";
15
- import { IDLE_MINUTES, readSettings, writeSettings } from "./settings.js";
15
+ import { type NotesSettings, readSettings, writeSettings } from "./settings.js";
16
+ import { openNotesSettings } from "./settings-screen.js";
16
17
  import { NotesStore } from "./store.js";
17
18
  import {
18
19
  createSharedExecutor,
@@ -26,9 +27,6 @@ import {
26
27
  windowDetails,
27
28
  } from "./windows.js";
28
29
 
29
- const HELP =
30
- "Use /notes status, /notes prune, /notes compact on|off, or /notes idle off|5|15|25|55 (minutes). Compaction and idle rollover default off. Compaction adds the normal installed Pi/PCC compaction flow to new_context. Idle intervals are not provider cache-expiry guarantees.";
31
-
32
30
  export default function notesCompaction(pi: ExtensionAPI): void {
33
31
  registerWindowRenderer(pi);
34
32
  const dir = join(getAgentDir(), "notes-compaction");
@@ -209,66 +207,49 @@ export default function notesCompaction(pi: ExtensionAPI): void {
209
207
  pi.registerCommand("notes", {
210
208
  description: "Manage local notes and context rollover",
211
209
  handler: async (args, ctx) => {
212
- const command = args.trim() || "status";
213
- if (command === "help") {
214
- ctx.ui.notify(HELP, "info");
210
+ if (args.trim()) {
211
+ ctx.ui.notify("Use /notes to open settings.", "info");
215
212
  return;
216
213
  }
217
- if (command === "status") {
218
- ctx.ui.notify(
219
- JSON.stringify({
220
- active: lifecycle.active,
221
- ...(unavailable ? { unavailable } : {}),
222
- normalCompaction: lifecycle.normalCompaction,
223
- idleMinutes: lifecycle.idleMinutes,
224
- ...store.status(),
225
- database: store.path,
226
- hybridLookup: Boolean(lifecycle.bridge?.lookup),
227
- encryptedDelivery: Boolean(lifecycle.bridge?.projectResult),
228
- }),
229
- "info",
230
- );
231
- return;
232
- }
233
- if (command === "compact on" || command === "compact off") {
234
- writeSettings(settingsPath, {
235
- normalCompaction: command === "compact on",
236
- idleMinutes: lifecycle.idleMinutes,
237
- });
238
- lifecycle.normalCompaction = command === "compact on";
239
- ctx.ui.notify(
240
- `Normal compaction ${lifecycle.normalCompaction ? "enabled" : "disabled"}.`,
241
- "info",
242
- );
214
+ if (!ctx.hasUI) {
215
+ ctx.ui.notify("Notes settings require interactive Pi.", "warning");
243
216
  return;
244
217
  }
245
- const idle = IDLE_MINUTES.find(
246
- (minutes) => command === `idle ${minutes === 0 ? "off" : minutes}`,
247
- );
248
- if (idle !== undefined) {
249
- writeSettings(settingsPath, {
250
- normalCompaction: lifecycle.normalCompaction,
251
- idleMinutes: idle,
252
- });
253
- lifecycle.idleMinutes = idle;
254
- ctx.ui.notify(
255
- idle === 0
256
- ? "Idle rollover disabled."
257
- : `Idle rollover after ${idle} minutes, when fresh notes are available. This is not a cache-expiry guarantee.`,
258
- "info",
259
- );
260
- return;
261
- }
262
- if (command === "prune") {
218
+ const save = (settings: NotesSettings) => {
219
+ writeSettings(settingsPath, settings);
220
+ Object.assign(lifecycle, settings);
221
+ };
222
+ const prune = async () => {
263
223
  await ctx.waitForIdle();
264
224
  store.updateFile(sessionRef(ctx));
225
+ const result = store.prune(ctx.sessionManager.getSessionId());
265
226
  ctx.ui.notify(
266
- JSON.stringify(store.prune(ctx.sessionManager.getSessionId())),
227
+ `Pruned ${result.removed} sessions. Retained ${result.unknownPaths} unknown paths and ${result.inaccessible} inaccessible files.`,
267
228
  "info",
268
229
  );
269
- return;
230
+ };
231
+ try {
232
+ await openNotesSettings(ctx, {
233
+ settings: () => ({
234
+ normalCompaction: lifecycle.normalCompaction,
235
+ idleMinutes: lifecycle.idleMinutes,
236
+ }),
237
+ save,
238
+ prune,
239
+ status: () => {
240
+ const counts = store.status();
241
+ return [
242
+ lifecycle.active
243
+ ? "Continuity active"
244
+ : `Continuity inactive: ${unavailable ?? "not initialized"}`,
245
+ `${counts.sessions} sessions · ${counts.revisions} note revisions · ${counts.cachedResponses} cached responses`,
246
+ `Hybrid lookup: ${lifecycle.bridge?.lookup ? "available" : "unavailable"} · Encrypted delivery: ${lifecycle.bridge?.projectResult ? "available" : "unavailable"}`,
247
+ ];
248
+ },
249
+ });
250
+ } catch (error) {
251
+ ctx.ui.notify(`Notes settings failed: ${String(error)}`, "error");
270
252
  }
271
- ctx.ui.notify(HELP, "warning");
272
253
  },
273
254
  });
274
255
  }
@@ -0,0 +1,115 @@
1
+ import {
2
+ type ExtensionContext,
3
+ getSettingsListTheme,
4
+ } from "@earendil-works/pi-coding-agent";
5
+ import { SettingsList, truncateToWidth } from "@earendil-works/pi-tui";
6
+ import { IDLE_MINUTES, type NotesSettings } from "./settings.js";
7
+
8
+ export async function openNotesSettings(
9
+ ctx: ExtensionContext,
10
+ options: {
11
+ settings: () => NotesSettings;
12
+ save: (settings: NotesSettings) => void;
13
+ status: () => string[];
14
+ prune: () => Promise<void>;
15
+ },
16
+ ): Promise<void> {
17
+ while (true) {
18
+ const status = options.status();
19
+ const action = await ctx.ui.custom<"close" | "prune">(
20
+ (tui, theme, _kb, done) => {
21
+ const settings = options.settings();
22
+ const idleValue = (minutes: number) =>
23
+ minutes === 0 ? "off" : `${minutes} min`;
24
+ const list = new SettingsList(
25
+ [
26
+ {
27
+ id: "compact",
28
+ label: "Normal compaction",
29
+ description:
30
+ "Include the installed Pi/PCC compaction flow on new_context. Off keeps notes-only rollover.",
31
+ currentValue: settings.normalCompaction ? "on" : "off",
32
+ values: ["off", "on"],
33
+ },
34
+ {
35
+ id: "idle",
36
+ label: "Idle rollover",
37
+ description:
38
+ "Roll over before the next input only with fresh notes from a completed turn. Not a cache-expiry guarantee.",
39
+ currentValue: idleValue(settings.idleMinutes),
40
+ values: IDLE_MINUTES.map(idleValue),
41
+ },
42
+ {
43
+ id: "prune",
44
+ label: "Prune missing sessions",
45
+ description:
46
+ "Remove stored records only for missing session files. Keeps active sessions, unknown paths and inaccessible files. Confirmation required.",
47
+ currentValue: "select…",
48
+ values: ["select…", "prune…"],
49
+ },
50
+ ],
51
+ 5,
52
+ getSettingsListTheme(),
53
+ (id, value) => {
54
+ if (id === "prune") {
55
+ done("prune");
56
+ return;
57
+ }
58
+ const current = options.settings();
59
+ try {
60
+ if (id === "compact")
61
+ options.save({ ...current, normalCompaction: value === "on" });
62
+ else {
63
+ const idleMinutes = IDLE_MINUTES.find(
64
+ (minutes) => idleValue(minutes) === value,
65
+ );
66
+ if (idleMinutes !== undefined)
67
+ options.save({ ...current, idleMinutes });
68
+ }
69
+ } catch (error) {
70
+ list.updateValue(
71
+ id,
72
+ id === "compact"
73
+ ? current.normalCompaction
74
+ ? "on"
75
+ : "off"
76
+ : idleValue(current.idleMinutes),
77
+ );
78
+ ctx.ui.notify(
79
+ `Could not save notes settings: ${String(error)}`,
80
+ "error",
81
+ );
82
+ }
83
+ tui.requestRender();
84
+ },
85
+ () => done("close"),
86
+ { enableSearch: false },
87
+ );
88
+ return {
89
+ render: (width) =>
90
+ [
91
+ theme.fg("accent", "Notes & context"),
92
+ ...status.map((line) => theme.fg("dim", line)),
93
+ "",
94
+ ...list.render(width),
95
+ "",
96
+ theme.fg(
97
+ "dim",
98
+ "↑↓ navigate · Enter/Space change · Esc close · saved immediately",
99
+ ),
100
+ ].map((line) => truncateToWidth(line, width)),
101
+ invalidate: () => list.invalidate(),
102
+ handleInput: (data) => list.handleInput(data),
103
+ };
104
+ },
105
+ );
106
+ if (action === "close") return;
107
+ if (
108
+ await ctx.ui.confirm(
109
+ "Prune missing sessions?",
110
+ "Delete local notes and cached responses for missing session files? The active session, unknown paths and inaccessible files are retained.",
111
+ )
112
+ )
113
+ await options.prune();
114
+ }
115
+ }
package/src/settings.ts CHANGED
@@ -25,7 +25,7 @@ export function readSettings(path: string): NotesSettings {
25
25
  typeof settings.normalCompaction !== "boolean"
26
26
  )
27
27
  throw new Error(
28
- "Invalid notes settings. Use /notes compact on or /notes compact off to replace them.",
28
+ "Invalid notes settings. Save a setting in /notes, then reload Pi.",
29
29
  );
30
30
  const idleMinutes = "idleMinutes" in settings ? settings.idleMinutes : 0;
31
31
  if (!IDLE_MINUTES.some((value) => value === idleMinutes))