@vanillagreen/pi-qol 1.7.0 → 1.7.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
CHANGED
|
@@ -13,6 +13,7 @@ Quality-of-life extension for Pi: compact statusline, multiline input, session n
|
|
|
13
13
|
- Newline-insert in the editor (multi-line drafts without auto-submit), with a fallback binding for terminals that can't distinguish the primary key.
|
|
14
14
|
- Sessions auto-name from your first prompt. `/rename` overrides anytime.
|
|
15
15
|
- `/search` browses previous sessions with snippet previews; the configured shortcut opens it instantly.
|
|
16
|
+
- Session search reads prompt snippets line-by-line, so very large session JSONL files do not have to be materialized just to browse, search, or import context.
|
|
16
17
|
- `/context` shows a Claude-style context-window breakdown.
|
|
17
18
|
- `/handoff <goal>` drafts a focused prompt for a new session.
|
|
18
19
|
- `/schedule 20m <message>` or `/schedule 1h45m <message>` sends a delayed prompt without invoking the model until the timer fires.
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SESSION_SEARCH_STATUS_KEY,
|
|
11
11
|
} from "../constants.js";
|
|
12
12
|
import { expandHome, piSettingsPaths, settingBoolean, settingNumber, settingString, settingStringAllowEmpty } from "../settings.js";
|
|
13
|
+
import { forEachSessionJsonlLine } from "./jsonl.js";
|
|
13
14
|
import type {
|
|
14
15
|
QolSessionPaletteAction,
|
|
15
16
|
QolSessionSearchPendingMessage,
|
|
@@ -166,15 +167,14 @@ export function sessionUserMessages(sessionPath: string): QolSessionUserMessage[
|
|
|
166
167
|
if (cached) return cached;
|
|
167
168
|
const messages: QolSessionUserMessage[] = [];
|
|
168
169
|
try {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (!line.trim()) continue;
|
|
170
|
+
forEachSessionJsonlLine(sessionPath, (line) => {
|
|
171
|
+
if (!line.trim()) return;
|
|
172
172
|
let entry: any;
|
|
173
|
-
try { entry = JSON.parse(line); } catch {
|
|
173
|
+
try { entry = JSON.parse(line); } catch { return; }
|
|
174
174
|
const message = entry?.type === "message" ? entry.message : undefined;
|
|
175
|
-
if (!message || message.role !== "user")
|
|
175
|
+
if (!message || message.role !== "user") return;
|
|
176
176
|
const text = oneLine(messageContentText(message.content));
|
|
177
|
-
if (!text)
|
|
177
|
+
if (!text) return;
|
|
178
178
|
messages.push({
|
|
179
179
|
entryId: typeof entry.id === "string" ? entry.id : undefined,
|
|
180
180
|
index: messages.length + 1,
|
|
@@ -182,7 +182,7 @@ export function sessionUserMessages(sessionPath: string): QolSessionUserMessage[
|
|
|
182
182
|
text,
|
|
183
183
|
timestamp: sessionMessageTimestamp(entry, message),
|
|
184
184
|
});
|
|
185
|
-
}
|
|
185
|
+
});
|
|
186
186
|
} catch {
|
|
187
187
|
// Ignore unreadable sessions; callers fall back to SessionInfo.firstMessage.
|
|
188
188
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
1
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
3
2
|
import { SessionManager, type ExtensionAPI, type ExtensionContext, type Theme } from "@earendil-works/pi-coding-agent";
|
|
4
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
import { settingNumber, settingString } from "../settings.js";
|
|
14
13
|
import { sessionDisplayName } from "./cache.js";
|
|
15
14
|
import { QolSessionSearchLoadingComponent } from "./component.js";
|
|
15
|
+
import { forEachSessionJsonlLine } from "./jsonl.js";
|
|
16
16
|
import type { QolSessionSearchPendingMessage, QolSessionSearchResult } from "./types.js";
|
|
17
17
|
|
|
18
18
|
export function trimSessionSummaryInput(text: string, cwd: string): string {
|
|
@@ -33,14 +33,13 @@ function messagesForSessionSummary(sessionPath: string): AgentMessage[] {
|
|
|
33
33
|
// Fall back to a direct JSONL parse below.
|
|
34
34
|
}
|
|
35
35
|
try {
|
|
36
|
-
const lines = readFileSync(sessionPath, "utf8").split(/\r?\n/);
|
|
37
36
|
const messages: AgentMessage[] = [];
|
|
38
|
-
|
|
39
|
-
if (!line.trim())
|
|
37
|
+
forEachSessionJsonlLine(sessionPath, (line) => {
|
|
38
|
+
if (!line.trim()) return;
|
|
40
39
|
let entry: any;
|
|
41
|
-
try { entry = JSON.parse(line); } catch {
|
|
40
|
+
try { entry = JSON.parse(line); } catch { return; }
|
|
42
41
|
if (entry?.type === "message" && entry.message) messages.push(entry.message);
|
|
43
|
-
}
|
|
42
|
+
});
|
|
44
43
|
return messages;
|
|
45
44
|
} catch {
|
|
46
45
|
return [];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { closeSync, openSync, readSync } from "node:fs";
|
|
2
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3
|
+
|
|
4
|
+
export function forEachSessionJsonlLine(sessionPath: string, onLine: (line: string) => void, chunkSize = 64 * 1024): void {
|
|
5
|
+
const fd = openSync(sessionPath, "r");
|
|
6
|
+
const buffer = Buffer.allocUnsafe(Math.max(1, Math.floor(chunkSize)));
|
|
7
|
+
const decoder = new StringDecoder("utf8");
|
|
8
|
+
let pending = "";
|
|
9
|
+
try {
|
|
10
|
+
for (;;) {
|
|
11
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
|
|
12
|
+
if (bytesRead === 0) break;
|
|
13
|
+
pending += decoder.write(buffer.subarray(0, bytesRead));
|
|
14
|
+
let start = 0;
|
|
15
|
+
for (;;) {
|
|
16
|
+
const newline = pending.indexOf("\n", start);
|
|
17
|
+
if (newline < 0) {
|
|
18
|
+
pending = pending.slice(start);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
const line = pending.slice(start, newline);
|
|
22
|
+
onLine(line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
23
|
+
start = newline + 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
pending += decoder.end();
|
|
27
|
+
if (pending.length > 0) onLine(pending.endsWith("\r") ? pending.slice(0, -1) : pending);
|
|
28
|
+
} finally {
|
|
29
|
+
closeSync(fd);
|
|
30
|
+
}
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanillagreen/pi-qol",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Pi quality-of-life extension: compact statusline/π prompt, reliable multiline input, styled pasted-image chips, session naming/search/context import, scheduled prompts, handoff, permission prompts, notifications, custom compaction, and a collapsed-thinking timer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|