@diegopetrucci/pi-extensions 0.1.32 → 0.1.34
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 -3
- package/extensions/brrr/README.md +84 -0
- package/extensions/brrr/brrr.example.json +12 -0
- package/extensions/brrr/index.ts +250 -0
- package/extensions/brrr/package.json +28 -0
- package/extensions/librarian/README.md +1 -1
- package/extensions/librarian/index.ts +2 -2
- package/extensions/librarian/package.json +9 -2
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
A collection of [pi](https://github.com/earendil-works/pi-mono) agent extensions I made.
|
|
4
4
|
|
|
5
|
-
**License note:** This root collection package is mixed-licensed: most of it is MIT, while [`extensions/review`](./extensions/review) is Apache-2.0 and includes its own [LICENSE](./extensions/review/LICENSE).
|
|
6
|
-
|
|
7
5
|
- [`agent-workflow-audit`](./extensions/agent-workflow-audit): Adds `/agent-workflow-audit`, which runs an isolated repo workflow audit subagent and returns only the final distilled report to the main session.
|
|
6
|
+
- [`brrr`](./extensions/brrr): Sends brrr push notifications when pi finishes an agent turn and is ready for input, with optional macOS idle gating.
|
|
8
7
|
- [`confirm-destructive`](./extensions/confirm-destructive): Confirms before destructive session actions like clear, switch, and fork.
|
|
9
8
|
- [`context-cap`](./extensions/context-cap): Caps effective model context windows at 200k tokens by default so pi avoids the `dumb zone`; toggle temporarily with `/context-cap`.
|
|
10
9
|
- [`context-inspector`](./extensions/context-inspector): Adds `/context`, a local self-contained HTML dashboard that breaks down where the current session context is going, with category overview, top offenders, and drilldown search.
|
|
11
10
|
- [`dirty-repo-guard`](./extensions/dirty-repo-guard): Prompts before new sessions, session switches, or forks when the current git repo has uncommitted changes.
|
|
12
11
|
- [`gnosis`](./extensions/gnosis): Exposes the `gn` repo-local knowledge base CLI as an agent tool for searching and recording durable project decisions, constraints, and intent.
|
|
13
12
|
- [`inline-bash`](./extensions/inline-bash): Expands `!{command}` snippets in user prompts by running them through bash before the prompt reaches the agent.
|
|
14
|
-
- [`librarian`](./extensions/librarian): Adds a GitHub research scout with a local repo checkout cache enabled by default under the OS user cache directory, toggleable with `/librarian-cache`, with cached repos expiring after
|
|
13
|
+
- [`librarian`](./extensions/librarian): Adds a GitHub research scout with a local repo checkout cache enabled by default under the OS user cache directory, toggleable with `/librarian-cache`, with cached repos expiring after 7 days of non-use.
|
|
15
14
|
- [`minimal-footer`](./extensions/minimal-footer): Replaces pi's built-in footer with a minimal configurable two-line layout: branch/repo on the first line, context/model on the second, optional `DUMB ZONE`, plus OpenAI Codex 5-hour and 7-day usage when available.
|
|
16
15
|
- [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input.
|
|
17
16
|
- [`openai-fast`](./extensions/openai-fast): Adds `/fast` to enable OpenAI Codex Fast mode for ChatGPT-auth GPT-5.4 and GPT-5.5 by injecting the priority service tier.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# brrr
|
|
2
|
+
|
|
3
|
+
A pi extension that sends [brrr](https://brrr.now) push notifications when pi finishes an agent turn and is ready for input.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
### Standalone npm package
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pi install npm:@diegopetrucci/pi-brrr
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Collection package
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pi install npm:@diegopetrucci/pi-extensions
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### GitHub package
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pi install git:github.com/diegopetrucci/pi-extensions
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then reload pi:
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
/reload
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configuration
|
|
32
|
+
|
|
33
|
+
Config files are merged, with project config overriding global config:
|
|
34
|
+
|
|
35
|
+
- `~/.pi/agent/extensions/brrr.json`
|
|
36
|
+
- `<project>/.pi/brrr.json`
|
|
37
|
+
|
|
38
|
+
The default config expects your webhook in `BRRR_WEBHOOK_URL`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export BRRR_WEBHOOK_URL='https://api.brrr.now/v1/br_...'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
A ready-to-copy sample file is included at [`brrr.example.json`](./brrr.example.json).
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"enabled": true,
|
|
51
|
+
"onlyWhenInteractive": true,
|
|
52
|
+
"webhook": "$BRRR_WEBHOOK_URL",
|
|
53
|
+
"idleSeconds": 20,
|
|
54
|
+
"title": "Pi finished",
|
|
55
|
+
"message": "Pi finished working in '{project}'.",
|
|
56
|
+
"includeLastAssistantMessage": true,
|
|
57
|
+
"sound": "",
|
|
58
|
+
"openUrl": "",
|
|
59
|
+
"imageUrl": ""
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Commands
|
|
64
|
+
|
|
65
|
+
- `/brrr` shows whether notifications are enabled, whether the webhook resolves, and the idle threshold.
|
|
66
|
+
|
|
67
|
+
## Config fields
|
|
68
|
+
|
|
69
|
+
- `enabled`: master on/off switch
|
|
70
|
+
- `onlyWhenInteractive`: skip notifications in print / non-UI mode
|
|
71
|
+
- `webhook`: brrr webhook URL or an environment reference like `$BRRR_WEBHOOK_URL`
|
|
72
|
+
- `idleSeconds`: only send when macOS has been idle for at least this many seconds; set to `null` to disable idle gating
|
|
73
|
+
- `title`: notification title; supports `{project}` and `{cwd}`
|
|
74
|
+
- `message`: fallback notification body; supports `{project}` and `{cwd}`
|
|
75
|
+
- `includeLastAssistantMessage`: use the final assistant message as the notification body when available
|
|
76
|
+
- `sound`: optional brrr sound value
|
|
77
|
+
- `openUrl`: optional URL to open from the notification
|
|
78
|
+
- `imageUrl`: optional image URL
|
|
79
|
+
|
|
80
|
+
## Notes
|
|
81
|
+
|
|
82
|
+
- Hooks the `agent_end` event.
|
|
83
|
+
- The extension sends directly to the brrr webhook; it does not require the `brrr` CLI at runtime.
|
|
84
|
+
- By default, notifications are skipped unless the Mac has been idle for at least 20 seconds.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"enabled": true,
|
|
3
|
+
"onlyWhenInteractive": true,
|
|
4
|
+
"webhook": "$BRRR_WEBHOOK_URL",
|
|
5
|
+
"idleSeconds": 20,
|
|
6
|
+
"title": "Pi finished",
|
|
7
|
+
"message": "Pi finished working in '{project}'.",
|
|
8
|
+
"includeLastAssistantMessage": true,
|
|
9
|
+
"sound": "",
|
|
10
|
+
"openUrl": "",
|
|
11
|
+
"imageUrl": ""
|
|
12
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi brrr Extension
|
|
3
|
+
*
|
|
4
|
+
* Sends a brrr push notification when pi finishes an agent turn and is ready
|
|
5
|
+
* for more input.
|
|
6
|
+
*
|
|
7
|
+
* Config files (project overrides global):
|
|
8
|
+
* - ~/.pi/agent/extensions/brrr.json
|
|
9
|
+
* - <cwd>/.pi/brrr.json
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFile } from "node:child_process";
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { basename, join } from "node:path";
|
|
15
|
+
import { promisify } from "node:util";
|
|
16
|
+
import { getAgentDir, type ExtensionAPI, type ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
17
|
+
|
|
18
|
+
const execFileAsync = promisify(execFile);
|
|
19
|
+
|
|
20
|
+
interface BrrrConfig {
|
|
21
|
+
enabled: boolean;
|
|
22
|
+
onlyWhenInteractive: boolean;
|
|
23
|
+
webhook: string;
|
|
24
|
+
idleSeconds: number | null;
|
|
25
|
+
title: string;
|
|
26
|
+
message: string;
|
|
27
|
+
includeLastAssistantMessage: boolean;
|
|
28
|
+
sound: string;
|
|
29
|
+
openUrl: string;
|
|
30
|
+
imageUrl: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface BrrrPayload {
|
|
34
|
+
message: string;
|
|
35
|
+
title?: string;
|
|
36
|
+
subtitle?: string;
|
|
37
|
+
expiration_date?: string;
|
|
38
|
+
sound?: string;
|
|
39
|
+
open_url?: string;
|
|
40
|
+
image_url?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const DEFAULT_CONFIG: BrrrConfig = {
|
|
44
|
+
enabled: true,
|
|
45
|
+
onlyWhenInteractive: true,
|
|
46
|
+
webhook: "$BRRR_WEBHOOK_URL",
|
|
47
|
+
idleSeconds: 20,
|
|
48
|
+
title: "Pi finished",
|
|
49
|
+
message: "Pi finished working in '{project}'.",
|
|
50
|
+
includeLastAssistantMessage: true,
|
|
51
|
+
sound: "",
|
|
52
|
+
openUrl: "",
|
|
53
|
+
imageUrl: "",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function readConfigFile(path: string): Partial<BrrrConfig> {
|
|
57
|
+
if (!existsSync(path)) return {};
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(readFileSync(path, "utf-8")) as Partial<BrrrConfig>;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(`Warning: Could not parse ${path}: ${error}`);
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function mergeConfig(base: BrrrConfig, overrides: Partial<BrrrConfig>): BrrrConfig {
|
|
68
|
+
return {
|
|
69
|
+
...base,
|
|
70
|
+
...overrides,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadConfig(cwd: string): BrrrConfig {
|
|
75
|
+
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "brrr.json"));
|
|
76
|
+
const projectConfig = readConfigFile(join(cwd, ".pi", "brrr.json"));
|
|
77
|
+
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveWebhook(raw: string): string | undefined {
|
|
81
|
+
const value = raw.trim();
|
|
82
|
+
if (!value) return undefined;
|
|
83
|
+
|
|
84
|
+
const envMatch = value.match(/^\$([A-Z0-9_]+)$/) ?? value.match(/^\$\{([A-Z0-9_]+)\}$/);
|
|
85
|
+
if (envMatch) return process.env[envMatch[1]]?.trim() || undefined;
|
|
86
|
+
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isBrrrWebhookUrl(value: string): boolean {
|
|
91
|
+
try {
|
|
92
|
+
const url = new URL(value);
|
|
93
|
+
return (
|
|
94
|
+
url.protocol === "https:" &&
|
|
95
|
+
(url.hostname === "api.brrr.now" || url.hostname === "dev.api.brrr.now") &&
|
|
96
|
+
/^\/v1\/br_[A-Za-z0-9_]+$/.test(url.pathname)
|
|
97
|
+
);
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function getMacOsIdleSeconds(): Promise<number | null> {
|
|
104
|
+
if (process.platform !== "darwin") return null;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const { stdout } = await execFileAsync("ioreg", ["-c", "IOHIDSystem"], {
|
|
108
|
+
timeout: 1000,
|
|
109
|
+
maxBuffer: 1024 * 1024,
|
|
110
|
+
});
|
|
111
|
+
const match = stdout.match(/HIDIdleTime"\s*=\s*(\d+)/) ?? stdout.match(/HIDIdleTime\s+=\s+(\d+)/);
|
|
112
|
+
if (!match) return null;
|
|
113
|
+
|
|
114
|
+
const idleNanoseconds = Number(match[1]);
|
|
115
|
+
if (!Number.isFinite(idleNanoseconds)) return null;
|
|
116
|
+
|
|
117
|
+
return Math.floor(idleNanoseconds / 1_000_000_000);
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function shouldSkipForIdleThreshold(idleSeconds: number | null): Promise<boolean> {
|
|
124
|
+
if (idleSeconds === null) return false;
|
|
125
|
+
if (!Number.isFinite(idleSeconds) || idleSeconds < 0) return false;
|
|
126
|
+
|
|
127
|
+
const currentIdleSeconds = await getMacOsIdleSeconds();
|
|
128
|
+
if (currentIdleSeconds === null) return false;
|
|
129
|
+
|
|
130
|
+
return currentIdleSeconds < idleSeconds;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function formatTemplate(template: string, cwd: string): string {
|
|
134
|
+
const project = basename(cwd || process.cwd());
|
|
135
|
+
return template.replaceAll("{project}", project).replaceAll("{cwd}", cwd);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractTextContent(content: unknown): string {
|
|
139
|
+
if (typeof content === "string") return content;
|
|
140
|
+
if (!Array.isArray(content)) return "";
|
|
141
|
+
|
|
142
|
+
const parts: string[] = [];
|
|
143
|
+
for (const part of content) {
|
|
144
|
+
if (typeof part === "string") {
|
|
145
|
+
parts.push(part);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (typeof part !== "object" || part === null) continue;
|
|
149
|
+
|
|
150
|
+
const maybeText = (part as { text?: unknown }).text;
|
|
151
|
+
if (typeof maybeText === "string") parts.push(maybeText);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return parts.join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function lastAssistantMessage(messages: readonly unknown[]): string | undefined {
|
|
158
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
159
|
+
const message = messages[index];
|
|
160
|
+
if (typeof message !== "object" || message === null) continue;
|
|
161
|
+
if ((message as { role?: unknown }).role !== "assistant") continue;
|
|
162
|
+
|
|
163
|
+
const text = extractTextContent((message as { content?: unknown }).content).trim();
|
|
164
|
+
if (text) return text;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function truncateMessage(value: string): string {
|
|
171
|
+
const trimmed = value.trim();
|
|
172
|
+
if (trimmed.length <= 800) return trimmed;
|
|
173
|
+
return `${trimmed.slice(0, 797).trimEnd()}...`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function sendBrrr(webhook: string, payload: BrrrPayload): Promise<{ ok: boolean; error?: string }> {
|
|
177
|
+
const controller = new AbortController();
|
|
178
|
+
const timeout = setTimeout(() => controller.abort(), 2_000);
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const body: Record<string, string> = { message: payload.message };
|
|
182
|
+
if (payload.title) body.title = payload.title;
|
|
183
|
+
if (payload.subtitle) body.subtitle = payload.subtitle;
|
|
184
|
+
if (payload.expiration_date) body.expiration_date = payload.expiration_date;
|
|
185
|
+
if (payload.sound) body.sound = payload.sound;
|
|
186
|
+
if (payload.open_url) body.open_url = payload.open_url;
|
|
187
|
+
if (payload.image_url) body.image_url = payload.image_url;
|
|
188
|
+
|
|
189
|
+
const response = await fetch(webhook, {
|
|
190
|
+
method: "POST",
|
|
191
|
+
headers: {
|
|
192
|
+
"content-type": "application/json",
|
|
193
|
+
accept: "application/json",
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify(body),
|
|
196
|
+
signal: controller.signal,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (response.status === 202) return { ok: true };
|
|
200
|
+
return { ok: false, error: `Unexpected response status ${response.status}.` };
|
|
201
|
+
} catch (error) {
|
|
202
|
+
return { ok: false, error: error instanceof Error ? error.message : "Unknown webhook failure." };
|
|
203
|
+
} finally {
|
|
204
|
+
clearTimeout(timeout);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function notify(ctx: ExtensionCommandContext, message: string, type: "info" | "warning" | "error" = "info"): void {
|
|
209
|
+
if (ctx.hasUI) ctx.ui.notify(message, type);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function describeConfig(config: BrrrConfig, webhook: string | undefined): string {
|
|
213
|
+
const webhookStatus = webhook ? (isBrrrWebhookUrl(webhook) ? "configured" : "invalid") : "missing";
|
|
214
|
+
const idle = config.idleSeconds === null ? "off" : `${config.idleSeconds}s`;
|
|
215
|
+
return `brrr is ${config.enabled ? "enabled" : "disabled"}; webhook ${webhookStatus}; idle threshold ${idle}.`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export default function brrrExtension(pi: ExtensionAPI) {
|
|
219
|
+
pi.registerCommand("brrr", {
|
|
220
|
+
description: "Show brrr notification status",
|
|
221
|
+
handler: async (_args, ctx) => {
|
|
222
|
+
const config = loadConfig(ctx.cwd);
|
|
223
|
+
notify(ctx, describeConfig(config, resolveWebhook(config.webhook)));
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
pi.on("agent_end", async (event, ctx) => {
|
|
228
|
+
const config = loadConfig(ctx.cwd);
|
|
229
|
+
if (!config.enabled) return;
|
|
230
|
+
if (config.onlyWhenInteractive && !ctx.hasUI) return;
|
|
231
|
+
|
|
232
|
+
const webhook = resolveWebhook(config.webhook);
|
|
233
|
+
if (!webhook || !isBrrrWebhookUrl(webhook)) return;
|
|
234
|
+
if (await shouldSkipForIdleThreshold(config.idleSeconds)) return;
|
|
235
|
+
|
|
236
|
+
const assistantMessage = config.includeLastAssistantMessage ? lastAssistantMessage(event.messages as readonly unknown[]) : undefined;
|
|
237
|
+
const message = truncateMessage(assistantMessage || formatTemplate(config.message, ctx.cwd));
|
|
238
|
+
const result = await sendBrrr(webhook, {
|
|
239
|
+
title: formatTemplate(config.title, ctx.cwd),
|
|
240
|
+
message,
|
|
241
|
+
sound: config.sound.trim() || undefined,
|
|
242
|
+
open_url: config.openUrl.trim() || undefined,
|
|
243
|
+
image_url: config.imageUrl.trim() || undefined,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (!result.ok && result.error) {
|
|
247
|
+
console.error(`brrr notification failed: ${result.error}`);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@diegopetrucci/pi-brrr",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A pi extension that sends brrr push notifications when pi is ready for input.",
|
|
5
|
+
"keywords": ["pi-package", "pi", "brrr", "notification", "push"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/diegopetrucci/pi-extensions.git",
|
|
10
|
+
"directory": "extensions/brrr"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"index.ts",
|
|
14
|
+
"README.md",
|
|
15
|
+
"brrr.example.json"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"pi": {
|
|
21
|
+
"extensions": [
|
|
22
|
+
"index.ts"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -37,7 +37,7 @@ Then reload pi:
|
|
|
37
37
|
- Uses `gh` for GitHub search/API access
|
|
38
38
|
- Uses cached local checkouts by default
|
|
39
39
|
- Toggle cache behavior for future calls with `/librarian-cache on | off | toggle | status`
|
|
40
|
-
- Cached repos are removed lazily after
|
|
40
|
+
- Cached repos are removed lazily after 7 days without use
|
|
41
41
|
|
|
42
42
|
## Commands
|
|
43
43
|
|
|
@@ -21,7 +21,7 @@ const MAX_TURNS = 10;
|
|
|
21
21
|
const MAX_TOOL_CALLS_TO_KEEP = 80;
|
|
22
22
|
const DEFAULT_BASH_TIMEOUT_SECONDS = 60;
|
|
23
23
|
const MAX_RUN_MS = 10 * 60 * 1000;
|
|
24
|
-
const CACHE_TTL_DAYS =
|
|
24
|
+
const CACHE_TTL_DAYS = 7;
|
|
25
25
|
const CACHE_TTL_MS = CACHE_TTL_DAYS * 24 * 60 * 60 * 1000;
|
|
26
26
|
const CACHE_METADATA_FILE = ".pi-librarian-cache.json";
|
|
27
27
|
const CACHE_MARKER_FILE = ".pi-librarian-cache-used";
|
|
@@ -560,7 +560,7 @@ export default function librarianExtension(pi: ExtensionAPI) {
|
|
|
560
560
|
name: "librarian",
|
|
561
561
|
label: "Librarian",
|
|
562
562
|
description:
|
|
563
|
-
"GitHub research scout for coding and personal-assistant tasks. Use when the answer likely lives in GitHub repos, exact repo/path locations are unknown, or you'd otherwise do exploratory gh search/tree probes plus local rg/read inspection. Librarian uses an optional
|
|
563
|
+
"GitHub research scout for coding and personal-assistant tasks. Use when the answer likely lives in GitHub repos, exact repo/path locations are unknown, or you'd otherwise do exploratory gh search/tree probes plus local rg/read inspection. Librarian uses an optional 7-day local checkout cache by default; toggle it with /librarian-cache.",
|
|
564
564
|
promptSnippet:
|
|
565
565
|
"Research GitHub repositories with evidence-first path and line citations; local checkout cache is enabled by default and user-toggleable with /librarian-cache.",
|
|
566
566
|
promptGuidelines: [
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-librarian",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A pi GitHub research scout with a toggleable local repo checkout cache under the user's OS cache directory.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi",
|
|
8
|
+
"github",
|
|
9
|
+
"research",
|
|
10
|
+
"subagent",
|
|
11
|
+
"cache"
|
|
12
|
+
],
|
|
6
13
|
"license": "MIT",
|
|
7
14
|
"repository": {
|
|
8
15
|
"type": "git",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "A collection of pi extensions for context management, workflow audits, review-comment triage, notifications, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, and model/provider helpers.",
|
|
3
|
+
"version": "0.1.34",
|
|
4
|
+
"description": "A collection of pi extensions for context management, workflow audits, review-comment triage, notifications, brrr push alerts, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, and model/provider helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"pi": {
|
|
35
35
|
"extensions": [
|
|
36
36
|
"./extensions/agent-workflow-audit/index.ts",
|
|
37
|
+
"./extensions/brrr/index.ts",
|
|
37
38
|
"./extensions/minimal-footer/index.ts",
|
|
38
39
|
"./extensions/oracle/index.ts",
|
|
39
40
|
"./extensions/context-cap/index.ts",
|
|
@@ -52,5 +53,13 @@
|
|
|
52
53
|
"./extensions/triage-comments/index.ts"
|
|
53
54
|
],
|
|
54
55
|
"image": "https://raw.githubusercontent.com/diegopetrucci/pi-extensions/main/assets/oracle-preview.svg"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@earendil-works/pi-ai": "^0.75.5",
|
|
59
|
+
"@earendil-works/pi-coding-agent": "^0.75.5",
|
|
60
|
+
"@earendil-works/pi-tui": "^0.75.5",
|
|
61
|
+
"@types/node": "^25.9.1",
|
|
62
|
+
"typebox": "^1.1.38",
|
|
63
|
+
"typescript": "^6.0.3"
|
|
55
64
|
}
|
|
56
65
|
}
|