@kvidzibo/pi-afterword 0.6.0 → 0.7.0
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 +5 -0
- package/README.md +10 -7
- package/index.ts +12 -6
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
- Show each afterword's generating model and thinking level beside its ID and date, and in details.
|
|
6
|
+
- Save generation thinking levels; automatically upgrade existing databases, leaving older levels unknown.
|
|
7
|
+
|
|
3
8
|
## 0.6.0
|
|
4
9
|
|
|
5
10
|
- First standalone Pi package, extracted without behavior changes from the local extension.
|
package/README.md
CHANGED
|
@@ -26,21 +26,24 @@ pi install npm:@kvidzibo/pi-afterword
|
|
|
26
26
|
|
|
27
27
|
Run `/reload` in existing sessions. If migrating from the standalone
|
|
28
28
|
`pi-afterword.ts`, remove its old `extensions` entry first: do not load both.
|
|
29
|
-
Existing notes stay in the same database;
|
|
29
|
+
Existing notes stay in the same database; schema upgrades are automatic.
|
|
30
30
|
|
|
31
31
|
## Use
|
|
32
32
|
|
|
33
33
|
At interactive startup, `Afterwords: N` shows the saved-note count without adding
|
|
34
34
|
model context. `/pi-afterword` opens a read-only list, newest first. Arrows select,
|
|
35
35
|
Page Up/Down scroll, Enter shows details, and Escape returns or closes. Browsing
|
|
36
|
-
makes no model requests and does not inject notes into the conversation.
|
|
36
|
+
makes no model requests and does not inject notes into the conversation. Each note's
|
|
37
|
+
header shows its generating model and thinking level; older notes without a saved
|
|
38
|
+
thinking level show `unknown`.
|
|
37
39
|
|
|
38
40
|
Generation requires **10 assistant turns on the active branch**, including
|
|
39
41
|
tool-call responses and compacted turns. Eligible interactive quit (including
|
|
40
42
|
handled SIGTERM/SIGHUP) and successful Pinote Done request one lesson of at most
|
|
41
|
-
30 whitespace-separated words from the current model, with minimal reasoning
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
30 whitespace-separated words from the current model, with minimal reasoning
|
|
44
|
+
(adjusted to supported levels; off for non-reasoning models) and no tools. It
|
|
45
|
+
prefers verified causes/fixes, surprising constraints or failed approaches;
|
|
46
|
+
otherwise it records an important decision and its rationale.
|
|
44
47
|
|
|
45
48
|
The request uses up to the last 64,000 conversation characters, less for small
|
|
46
49
|
context windows; images, thinking and system instructions are omitted. There is
|
|
@@ -62,8 +65,8 @@ service or runtime dependency is required. The directory and database use
|
|
|
62
65
|
owner-only permissions. Stop Pi sessions before backing up or moving the database.
|
|
63
66
|
|
|
64
67
|
The database is **local and unencrypted**. Notes and metadata (session ID,
|
|
65
|
-
conversation hash, project directory, model, timestamp, trigger
|
|
66
|
-
ID) can contain sensitive project information despite the no-secrets instruction.
|
|
68
|
+
conversation hash, project directory, model, thinking level, timestamp, trigger
|
|
69
|
+
and optional task ID) can contain sensitive project information despite the no-secrets instruction.
|
|
67
70
|
Generation sends conversation excerpts to your configured model provider.
|
|
68
71
|
|
|
69
72
|
## Development
|
package/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { chmodSync, closeSync, mkdirSync, openSync, statSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { clampThinkingLevel } from "@earendil-works/pi-ai";
|
|
4
5
|
import { convertToLlm, DynamicBorder, ExtensionSelectorComponent, getAgentDir, keyHint, rawKeyHint, serializeConversation, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
6
|
import { Container, ScrollView, Spacer, Text } from "@earendil-works/pi-tui";
|
|
6
7
|
|
|
@@ -31,10 +32,14 @@ async function database() {
|
|
|
31
32
|
reason TEXT NOT NULL,
|
|
32
33
|
cwd TEXT NOT NULL,
|
|
33
34
|
model TEXT NOT NULL,
|
|
35
|
+
thinking_level TEXT,
|
|
34
36
|
created_at TEXT NOT NULL,
|
|
35
37
|
note TEXT NOT NULL,
|
|
36
38
|
UNIQUE (session_id, context_hash)
|
|
37
39
|
)`);
|
|
40
|
+
if (!db.prepare("PRAGMA table_info(afterwords)").all().some((column) => column.name === "thinking_level")) {
|
|
41
|
+
db.exec("ALTER TABLE afterwords ADD COLUMN thinking_level TEXT");
|
|
42
|
+
}
|
|
38
43
|
return db;
|
|
39
44
|
} catch (error) { db.close(); throw error; }
|
|
40
45
|
}
|
|
@@ -70,12 +75,13 @@ async function record(ctx: ExtensionContext, state: NonNullable<ReturnType<typeo
|
|
|
70
75
|
const maxChars = Math.min(CONTEXT_CHARS, Math.floor((model.contextWindow - 2048) / 2));
|
|
71
76
|
if (maxChars < 256) throw new Error("Session context window too small");
|
|
72
77
|
const history = conversation.length > maxChars ? `[Earlier conversation omitted]\n${conversation.slice(-maxChars)}` : conversation;
|
|
78
|
+
const thinkingLevel = clampThinkingLevel(model, "minimal");
|
|
73
79
|
const result = await ctx.modelRegistry.streamSimple(model, {
|
|
74
80
|
messages: [
|
|
75
81
|
{ role: "system", content: INSTRUCTION, timestamp: Date.now() },
|
|
76
82
|
{ role: "user", content: history, timestamp: Date.now() },
|
|
77
83
|
],
|
|
78
|
-
}, { reasoning: "
|
|
84
|
+
}, { reasoning: thinkingLevel === "off" ? undefined : thinkingLevel, maxTokens: 1024, cacheRetention: "none", sessionId: randomUUID(), signal }).result();
|
|
79
85
|
check();
|
|
80
86
|
if (result.stopReason !== "stop") throw new Error("Incomplete afterword");
|
|
81
87
|
const note = result.content.filter((block) => block.type === "text").map((block) => block.text).join(" ").replace(/\s+/gu, " ").trim();
|
|
@@ -84,8 +90,8 @@ async function record(ctx: ExtensionContext, state: NonNullable<ReturnType<typeo
|
|
|
84
90
|
try {
|
|
85
91
|
db.exec("BEGIN IMMEDIATE"); // Never wait for locks during shutdown.
|
|
86
92
|
check();
|
|
87
|
-
db.prepare("INSERT OR IGNORE INTO afterwords (session_id, context_hash, task_id, reason, cwd, model, created_at, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
|
|
88
|
-
.run(sessionId, hash, taskId ?? null, reason, cwd, `${model.provider}/${model.id}`, new Date().toISOString(), note);
|
|
93
|
+
db.prepare("INSERT OR IGNORE INTO afterwords (session_id, context_hash, task_id, reason, cwd, model, thinking_level, created_at, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
|
94
|
+
.run(sessionId, hash, taskId ?? null, reason, cwd, `${result.provider ?? model.provider}/${result.responseModel ?? result.model ?? model.id}`, result.thinkingLevel ?? thinkingLevel, new Date().toISOString(), note);
|
|
89
95
|
check();
|
|
90
96
|
db.exec("COMMIT");
|
|
91
97
|
} catch (error) {
|
|
@@ -107,7 +113,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
107
113
|
try {
|
|
108
114
|
const db = await database();
|
|
109
115
|
try {
|
|
110
|
-
notes = db.prepare("SELECT id, note, created_at, cwd, reason, task_id FROM afterwords ORDER BY created_at DESC, id DESC").all();
|
|
116
|
+
notes = db.prepare("SELECT id, note, created_at, cwd, reason, task_id, model, thinking_level FROM afterwords ORDER BY created_at DESC, id DESC").all();
|
|
111
117
|
} finally { db.close(); }
|
|
112
118
|
} catch {
|
|
113
119
|
ctx.ui.notify("Saved afterwords could not be read.", "error");
|
|
@@ -153,7 +159,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
153
159
|
for (const [i, row] of notes.entries()) {
|
|
154
160
|
starts.push(lines.length);
|
|
155
161
|
const active = i === selected;
|
|
156
|
-
const label = theme.fg(active ? "accent" : "muted", `#${row.id} · ${String(row.created_at).slice(0, 16).replace("T", " ")}`);
|
|
162
|
+
const label = theme.fg(active ? "accent" : "muted", `#${row.id} · ${String(row.created_at).slice(0, 16).replace("T", " ")} · ${row.model} · thinking: ${row.thinking_level ?? "unknown"}`);
|
|
157
163
|
const text = new Text(`${label}\n${theme.fg(active ? "accent" : "text", String(row.note))}`, 0, 0);
|
|
158
164
|
for (const [j, line] of text.render(width - indent).entries()) {
|
|
159
165
|
const prefix = active && j === 0 && indent === 3 ? theme.fg("accent", " → ") : " ".repeat(indent);
|
|
@@ -198,7 +204,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
198
204
|
if (index === undefined) return;
|
|
199
205
|
const row = notes[index];
|
|
200
206
|
await ctx.ui.custom<void>((tui, _theme, keybindings, done) => {
|
|
201
|
-
const text = new Text(`${row.note}\n\n${row.created_at} · ${row.reason}${row.task_id === null ? "" : ` · task ${row.task_id}`}\n${row.cwd}`, 0, 0);
|
|
207
|
+
const text = new Text(`${row.note}\n\n${row.created_at} · ${row.model} · thinking: ${row.thinking_level ?? "unknown"}\n${row.reason}${row.task_id === null ? "" : ` · task ${row.task_id}`}\n${row.cwd}`, 0, 0);
|
|
202
208
|
const scroll = new ScrollView(text, { scrollbar: "hidden" });
|
|
203
209
|
return {
|
|
204
210
|
capturesViewportInput: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kvidzibo/pi-afterword",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Save durable conversation lessons on Pi quit or Pinote Done, and browse them with /pi-afterword.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,10 +41,14 @@
|
|
|
41
41
|
"prepublishOnly": "npm test"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
+
"@earendil-works/pi-ai": "*",
|
|
44
45
|
"@earendil-works/pi-coding-agent": "*",
|
|
45
46
|
"@earendil-works/pi-tui": "*"
|
|
46
47
|
},
|
|
47
48
|
"peerDependenciesMeta": {
|
|
49
|
+
"@earendil-works/pi-ai": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
48
52
|
"@earendil-works/pi-coding-agent": {
|
|
49
53
|
"optional": true
|
|
50
54
|
},
|
|
@@ -53,6 +57,7 @@
|
|
|
53
57
|
}
|
|
54
58
|
},
|
|
55
59
|
"devDependencies": {
|
|
60
|
+
"@earendil-works/pi-ai": "1.5.0",
|
|
56
61
|
"@earendil-works/pi-coding-agent": "1.5.0",
|
|
57
62
|
"@earendil-works/pi-tui": "1.5.0"
|
|
58
63
|
}
|