@vshulcz/pi-deja 0.21.7 → 0.22.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/README.md +2 -2
- package/index.ts +31 -15
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
English | [中文](https://github.com/vshulcz/deja-vu/blob/main/extensions/pi/docs/zh.md)
|
|
4
4
|
|
|
5
5
|
pi remembers its own sessions. This extension answers the other question: what
|
|
6
|
-
was done in the
|
|
6
|
+
was done in the forty other coding agents on this machine — Claude Code,
|
|
7
7
|
Codex, Cursor, Gemini, OpenClaw and Hermes among them — including the months
|
|
8
8
|
before pi was installed.
|
|
9
9
|
|
|
10
10
|
It runs [deja](https://github.com/vshulcz/deja-vu), a local Go binary that
|
|
11
11
|
indexes the transcripts those agents already wrote to disk. No LLM, no
|
|
12
|
-
embeddings, no network path
|
|
12
|
+
embeddings, no network path besides a daily release check, which DEJA_OFFLINE=1 turns off.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
package/index.ts
CHANGED
|
@@ -75,7 +75,10 @@ export default function (pi: any) {
|
|
|
75
75
|
if (installerExtensionPaths(homedir()).some((p) => existsSync(p))) return;
|
|
76
76
|
} catch {}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
// Once per session, not once per process: /new, a resume and a fork keep the
|
|
79
|
+
// process and switch the session, and a flag set on the first one left every
|
|
80
|
+
// later session with no digest.
|
|
81
|
+
const injected = new Set<string>();
|
|
79
82
|
let toldBuilding = false;
|
|
80
83
|
// The tool and compaction events carry no session id of their own, and
|
|
81
84
|
// recall dedupes per session: without one it repeats itself and forgets
|
|
@@ -123,17 +126,17 @@ export default function (pi: any) {
|
|
|
123
126
|
|
|
124
127
|
pi.on("before_agent_start", async (event: any, ctx: any) => {
|
|
125
128
|
try {
|
|
126
|
-
|
|
129
|
+
const key = sessionKey(event, ctx);
|
|
130
|
+
if (key) session = key;
|
|
131
|
+
if (!injected.has(session)) {
|
|
127
132
|
// The session goes with it: hook-context marks the one starting as
|
|
128
133
|
// live, which keeps it out of its own MCP recall on this first turn
|
|
129
134
|
// (#4394, as #4246 and #4273 did for Hermes and opencode).
|
|
130
|
-
const key = sessionKey(event, ctx);
|
|
131
|
-
if (key) session = key;
|
|
132
135
|
const { context: digest, receipt } = contextText(
|
|
133
|
-
run(["hook-context"], JSON.stringify({ session_id:
|
|
136
|
+
run(["hook-context"], JSON.stringify({ session_id: session, cwd: process.cwd() })),
|
|
134
137
|
);
|
|
135
138
|
if (digest) {
|
|
136
|
-
injected
|
|
139
|
+
injected.add(session);
|
|
137
140
|
ctx.ui.setStatus("deja", "");
|
|
138
141
|
// The receipt is what tells the user memory arrived; without it the
|
|
139
142
|
// recall is invisible and reads as the model guessing.
|
|
@@ -152,9 +155,7 @@ export default function (pi: any) {
|
|
|
152
155
|
}
|
|
153
156
|
return;
|
|
154
157
|
}
|
|
155
|
-
const
|
|
156
|
-
if (key) session = key;
|
|
157
|
-
const raw = run(["hook-prompt"], JSON.stringify({ prompt: event.prompt || "", session_id: key }));
|
|
158
|
+
const raw = run(["hook-prompt"], JSON.stringify({ prompt: event.prompt || "", session_id: session }));
|
|
158
159
|
if (!raw) return;
|
|
159
160
|
const resp = JSON.parse(raw);
|
|
160
161
|
if (resp && resp.systemMessage) ctx.ui.notify(resp.systemMessage, "info");
|
|
@@ -183,15 +184,16 @@ export default function (pi: any) {
|
|
|
183
184
|
// and pi has no handler that runs earlier whose return the model reads.
|
|
184
185
|
// So the file's own history goes out here: what was decided about it,
|
|
185
186
|
// from the sessions that decided it. deja answers once per session per
|
|
186
|
-
// fact, so re-reading the same file stays quiet.
|
|
187
|
-
|
|
187
|
+
// fact, so re-reading the same file stays quiet. An edit or a write
|
|
188
|
+
// without a read before it gets the same line, for the edits after it.
|
|
189
|
+
if (event.toolName === "read" || event.toolName === "edit" || event.toolName === "write") {
|
|
188
190
|
const path = String((event.input && (event.input.path || event.input.file_path)) || "");
|
|
189
191
|
if (!path) return;
|
|
190
192
|
const parts = Array.isArray(event.content) ? event.content : [];
|
|
191
|
-
const id = "
|
|
193
|
+
const id = "file:" + String(event.toolCallId || "");
|
|
192
194
|
if (!(id in repaired)) {
|
|
193
195
|
repaired[id] = run(["hook-tool", "--plain"], JSON.stringify({
|
|
194
|
-
tool_name:
|
|
196
|
+
tool_name: event.toolName,
|
|
195
197
|
tool_input: { file_path: path },
|
|
196
198
|
session_id: session,
|
|
197
199
|
cwd: process.cwd(),
|
|
@@ -230,12 +232,26 @@ export default function (pi: any) {
|
|
|
230
232
|
// Compaction throws away the blocks this session was shown, and the list
|
|
231
233
|
// that stops them repeating outlives them. session_compact fires once the
|
|
232
234
|
// summary has replaced the history, which is exactly when the session should
|
|
233
|
-
// be allowed to see those blocks again.
|
|
235
|
+
// be allowed to see those blocks again. The session file keeps the turns the
|
|
236
|
+
// summary replaced, so deja reads them from it and the next prompt carries
|
|
237
|
+
// what the agent was in the middle of.
|
|
234
238
|
pi.on("session_compact", async (_event: any, ctx: any) => {
|
|
235
239
|
try {
|
|
236
240
|
const key = sessionKey(_event, ctx);
|
|
237
241
|
if (key) session = key;
|
|
238
|
-
|
|
242
|
+
const m = ctx && ctx.sessionManager;
|
|
243
|
+
const file = m && m.getSessionFile ? String(m.getSessionFile() || "") : "";
|
|
244
|
+
run(["hook-precompact"], JSON.stringify({ session_id: session, transcript_path: file, cwd: process.cwd() }));
|
|
245
|
+
} catch {}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// The session is over: quit, /new, /resume, /fork or a reload. Its live
|
|
249
|
+
// stamp goes with it, so the next session's MCP recall can answer with it
|
|
250
|
+
// now rather than twenty minutes from now. The id is the one held so far;
|
|
251
|
+
// on a switch ctx may already name the next session.
|
|
252
|
+
pi.on("session_shutdown", async (_event: any) => {
|
|
253
|
+
try {
|
|
254
|
+
if (session) run(["hook-session-end"], JSON.stringify({ session_id: session }), 2000);
|
|
239
255
|
} catch {}
|
|
240
256
|
});
|
|
241
257
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vshulcz/pi-deja",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "deja-vu memory for pi: the coding sessions already on this machine — pi, Claude Code, Codex, Cursor and
|
|
3
|
+
"version": "0.22.0",
|
|
4
|
+
"description": "deja-vu memory for pi: the coding sessions already on this machine — pi, Claude Code, Codex, Cursor and 37 more — recalled at session start and on every prompt, searchable with /deja. A local index, no LLM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"index.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"node": ">=18"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@vshulcz/deja-vu": "^0.
|
|
48
|
+
"@vshulcz/deja-vu": "^0.22.0"
|
|
49
49
|
},
|
|
50
50
|
"pi": {
|
|
51
51
|
"extensions": [
|