my-pi 0.0.2 → 0.0.3
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/package.json +1 -1
- package/src/extensions/recall.ts +29 -226
package/package.json
CHANGED
package/src/extensions/recall.ts
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
|
-
// Recall extension —
|
|
2
|
-
//
|
|
1
|
+
// Recall extension — nudge the agent to use pirecall for past session context
|
|
2
|
+
// The model uses `npx pirecall` via bash directly — no custom tools needed
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
type ExtensionAPI,
|
|
6
|
-
defineTool,
|
|
7
|
-
} from '@mariozechner/pi-coding-agent';
|
|
8
|
-
import { Type } from '@sinclair/typebox';
|
|
4
|
+
import type { ExtensionAPI } from '@mariozechner/pi-coding-agent';
|
|
9
5
|
import { execFileSync } from 'node:child_process';
|
|
10
6
|
import { existsSync } from 'node:fs';
|
|
11
7
|
import { join } from 'node:path';
|
|
12
8
|
|
|
13
9
|
const DEFAULT_DB_PATH = join(process.env.HOME!, '.pi', 'pirecall.db');
|
|
14
10
|
|
|
15
|
-
function run_pirecall(
|
|
16
|
-
args: string[],
|
|
17
|
-
): { ok: true; data: unknown } | { ok: false; error: string } {
|
|
18
|
-
try {
|
|
19
|
-
const output = execFileSync('npx', ['pirecall', ...args], {
|
|
20
|
-
encoding: 'utf-8',
|
|
21
|
-
timeout: 15_000,
|
|
22
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
23
|
-
});
|
|
24
|
-
return { ok: true, data: JSON.parse(output) };
|
|
25
|
-
} catch (err) {
|
|
26
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
27
|
-
return { ok: false, error: message };
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
11
|
// Default export for Pi Package / additionalExtensionPaths loading
|
|
32
12
|
export default async function recall(pi: ExtensionAPI) {
|
|
33
13
|
// Sync on startup if db exists
|
|
@@ -43,209 +23,32 @@ export default async function recall(pi: ExtensionAPI) {
|
|
|
43
23
|
}
|
|
44
24
|
}
|
|
45
25
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Type.String({
|
|
72
|
-
description: 'Filter by project path (substring match)',
|
|
73
|
-
}),
|
|
74
|
-
),
|
|
75
|
-
}),
|
|
76
|
-
execute: async (_id, params) => {
|
|
77
|
-
const args = ['recall', params.query, '--json'];
|
|
78
|
-
if (params.limit) args.push('--limit', String(params.limit));
|
|
79
|
-
if (params.context)
|
|
80
|
-
args.push('--context', String(params.context));
|
|
81
|
-
if (params.project) args.push('--project', params.project);
|
|
82
|
-
|
|
83
|
-
const result = run_pirecall(args);
|
|
84
|
-
|
|
85
|
-
if (!result.ok) {
|
|
86
|
-
return {
|
|
87
|
-
content: [
|
|
88
|
-
{
|
|
89
|
-
type: 'text' as const,
|
|
90
|
-
text: `Recall failed: ${result.error}`,
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
details: {},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
content: [
|
|
99
|
-
{
|
|
100
|
-
type: 'text' as const,
|
|
101
|
-
text: JSON.stringify(result.data, null, 2),
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
details: {},
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
}),
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
// ── Tool: recall_search (full-text search with more options) ──
|
|
111
|
-
|
|
112
|
-
pi.registerTool(
|
|
113
|
-
defineTool({
|
|
114
|
-
name: 'recall_search',
|
|
115
|
-
label: 'Search Past Sessions',
|
|
116
|
-
description:
|
|
117
|
-
'Full-text search across all past session messages. More detailed than recall — returns individual message matches with metadata. Use for specific lookups.',
|
|
118
|
-
parameters: Type.Object({
|
|
119
|
-
query: Type.String({
|
|
120
|
-
description:
|
|
121
|
-
'Search term — supports FTS5: AND, OR, NOT, "phrase", prefix*',
|
|
122
|
-
}),
|
|
123
|
-
limit: Type.Optional(
|
|
124
|
-
Type.Number({
|
|
125
|
-
description: 'Maximum results (default: 20)',
|
|
126
|
-
}),
|
|
127
|
-
),
|
|
128
|
-
project: Type.Optional(
|
|
129
|
-
Type.String({
|
|
130
|
-
description: 'Filter by project path',
|
|
131
|
-
}),
|
|
132
|
-
),
|
|
133
|
-
session: Type.Optional(
|
|
134
|
-
Type.String({
|
|
135
|
-
description: 'Filter by session ID (prefix match)',
|
|
136
|
-
}),
|
|
137
|
-
),
|
|
138
|
-
after: Type.Optional(
|
|
139
|
-
Type.String({
|
|
140
|
-
description:
|
|
141
|
-
'Only results after date (ISO format, e.g. 2026-04-06)',
|
|
142
|
-
}),
|
|
143
|
-
),
|
|
144
|
-
sort: Type.Optional(
|
|
145
|
-
Type.String({
|
|
146
|
-
description: 'Sort: relevance (default), time, time-asc',
|
|
147
|
-
}),
|
|
148
|
-
),
|
|
149
|
-
}),
|
|
150
|
-
execute: async (_id, params) => {
|
|
151
|
-
const args = ['search', params.query, '--json'];
|
|
152
|
-
if (params.limit) args.push('--limit', String(params.limit));
|
|
153
|
-
if (params.project) args.push('--project', params.project);
|
|
154
|
-
if (params.session) args.push('--session', params.session);
|
|
155
|
-
if (params.after) args.push('--after', params.after);
|
|
156
|
-
if (params.sort) args.push('--sort', params.sort);
|
|
157
|
-
|
|
158
|
-
const result = run_pirecall(args);
|
|
159
|
-
|
|
160
|
-
if (!result.ok) {
|
|
161
|
-
return {
|
|
162
|
-
content: [
|
|
163
|
-
{
|
|
164
|
-
type: 'text' as const,
|
|
165
|
-
text: `Search failed: ${result.error}`,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
details: {},
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
content: [
|
|
174
|
-
{
|
|
175
|
-
type: 'text' as const,
|
|
176
|
-
text: JSON.stringify(result.data, null, 2),
|
|
177
|
-
},
|
|
178
|
-
],
|
|
179
|
-
details: {},
|
|
180
|
-
};
|
|
181
|
-
},
|
|
182
|
-
}),
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
// ── Command: /recall (user-facing) ──
|
|
186
|
-
|
|
187
|
-
pi.registerCommand('recall', {
|
|
188
|
-
description:
|
|
189
|
-
'Search past sessions — /recall <query> [--limit N] [--project path]',
|
|
190
|
-
handler: async (args, ctx) => {
|
|
191
|
-
const query = args.trim();
|
|
192
|
-
if (!query) {
|
|
193
|
-
ctx.ui.notify('Usage: /recall <search query>', 'warning');
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Sync first
|
|
198
|
-
ctx.ui.notify('Syncing sessions...');
|
|
199
|
-
run_pirecall(['sync', '--json']);
|
|
200
|
-
|
|
201
|
-
const result = run_pirecall([
|
|
202
|
-
'recall',
|
|
203
|
-
query,
|
|
204
|
-
'--json',
|
|
205
|
-
'--limit',
|
|
206
|
-
'5',
|
|
207
|
-
'--context',
|
|
208
|
-
'2',
|
|
209
|
-
]);
|
|
210
|
-
|
|
211
|
-
if (!result.ok) {
|
|
212
|
-
ctx.ui.notify(`Recall failed: ${result.error}`, 'error');
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Paste results into the editor for the user to send
|
|
217
|
-
const data = result.data as {
|
|
218
|
-
matches?: Array<{
|
|
219
|
-
session_id: string;
|
|
220
|
-
project: string;
|
|
221
|
-
date: string;
|
|
222
|
-
messages: Array<{
|
|
223
|
-
role: string;
|
|
224
|
-
text: string;
|
|
225
|
-
}>;
|
|
226
|
-
}>;
|
|
26
|
+
// System prompt hint so the model knows pirecall exists
|
|
27
|
+
pi.on(
|
|
28
|
+
'before_agent_start',
|
|
29
|
+
async (event: { systemPrompt: string }) => {
|
|
30
|
+
return {
|
|
31
|
+
systemPrompt:
|
|
32
|
+
event.systemPrompt +
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
## Session Recall
|
|
36
|
+
|
|
37
|
+
You have access to past Pi session history via \`npx pirecall\`. Use it when:
|
|
38
|
+
- The user references prior work ("what did we do", "last time", "remember when")
|
|
39
|
+
- You need context from a previous session about this project
|
|
40
|
+
- You want to avoid repeating work already done
|
|
41
|
+
|
|
42
|
+
Quick reference:
|
|
43
|
+
- \`npx pirecall recall "<query>" --json\` — LLM-optimised context retrieval with surrounding messages
|
|
44
|
+
- \`npx pirecall search "<query>" --json\` — full-text search (supports FTS5: AND, OR, NOT, "phrase", prefix*)
|
|
45
|
+
- \`npx pirecall search "<query>" --json --project my-pi\` — filter by project
|
|
46
|
+
- \`npx pirecall search "<query>" --json --after 2026-04-10\` — filter by date
|
|
47
|
+
- \`npx pirecall sessions --json\` — list recent sessions
|
|
48
|
+
- \`npx pirecall stats --json\` — database statistics
|
|
49
|
+
|
|
50
|
+
Always pass \`--json\` for structured output.`,
|
|
227
51
|
};
|
|
228
|
-
|
|
229
|
-
if (!data.matches || data.matches.length === 0) {
|
|
230
|
-
ctx.ui.notify(`No results for "${query}"`, 'warning');
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const formatted = data.matches
|
|
235
|
-
.map((m) => {
|
|
236
|
-
const msgs = m.messages
|
|
237
|
-
.map(
|
|
238
|
-
(msg) =>
|
|
239
|
-
` [${msg.role}] ${msg.text?.slice(0, 300) || '(empty)'}`,
|
|
240
|
-
)
|
|
241
|
-
.join('\n');
|
|
242
|
-
return `## ${m.project} (${m.date})\nSession: ${m.session_id.slice(0, 8)}\n${msgs}`;
|
|
243
|
-
})
|
|
244
|
-
.join('\n\n---\n\n');
|
|
245
|
-
|
|
246
|
-
ctx.ui.pasteToEditor(
|
|
247
|
-
`Here is relevant context from past sessions about "${query}":\n\n${formatted}`,
|
|
248
|
-
);
|
|
249
52
|
},
|
|
250
|
-
|
|
53
|
+
);
|
|
251
54
|
}
|