@diegopetrucci/pi-extensions 0.1.46 → 0.1.48
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 +1 -1
- package/extensions/brrr/README.md +2 -0
- package/extensions/brrr/index.ts +17 -5
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/README.md +1 -1
- package/extensions/claude-fast/index.ts +17 -6
- package/extensions/claude-fast/package.json +1 -1
- package/extensions/minimal-footer/README.md +29 -0
- package/extensions/minimal-footer/index.ts +46 -5
- package/extensions/minimal-footer/minimal-footer.example.json +5 -0
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/README.md +2 -0
- package/extensions/notify/index.ts +17 -4
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/README.md +1 -1
- package/extensions/openai-fast/index.ts +17 -6
- package/extensions/openai-fast/package.json +1 -1
- package/extensions/review/README.md +1 -1
- package/extensions/review/index.ts +15 -4
- package/extensions/review/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ A collection of [pi](https://github.com/earendil-works/pi-mono) agent extensions
|
|
|
16
16
|
- [`inline-bash`](./extensions/inline-bash): Expands `!{command}` snippets in user prompts by running them through bash before the prompt reaches the agent.
|
|
17
17
|
- [`illustrations-to-explain-things`](./extensions/illustrations-to-explain-things): Adds a skill for generating clean, absurd Xiaohei-style article illustrations, shot lists, image edits, and visual metaphors.
|
|
18
18
|
- [`librarian`](./extensions/librarian): Adds a GitHub research scout with a local repo checkout cache disabled by default under the OS user cache directory, toggleable with `/librarian-cache`, configurable subagent model/thinking defaults via `/librarian-config`, and cached repos expiring after 7 days of non-use.
|
|
19
|
-
- [`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.
|
|
19
|
+
- [`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`, optional `xp` marker, plus OpenAI Codex 5-hour and 7-day usage when available.
|
|
20
20
|
- [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input.
|
|
21
21
|
- [`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.
|
|
22
22
|
- [`oracle`](./extensions/oracle): Adds an Amp-style read-only oracle tool that auto-selects the strongest reasoning model on the current provider/subscription, supports persisted `/oracle` model/thinking defaults, requests xhigh reasoning by default and clamps to model capabilities, and shows live status while running.
|
|
@@ -35,6 +35,8 @@ Config files are merged, with project config overriding global config:
|
|
|
35
35
|
- `~/.pi/agent/extensions/brrr.json`
|
|
36
36
|
- `<project>/.pi/brrr.json`
|
|
37
37
|
|
|
38
|
+
Project config is only read after Pi reports that the project is trusted.
|
|
39
|
+
|
|
38
40
|
The default config expects your webhook in `BRRR_WEBHOOK_URL`:
|
|
39
41
|
|
|
40
42
|
```bash
|
package/extensions/brrr/index.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Config files (project overrides global):
|
|
8
8
|
* - ~/.pi/agent/extensions/brrr.json
|
|
9
|
-
* - <cwd>/.pi/brrr.json
|
|
9
|
+
* - <cwd>/.pi/brrr.json, when the project is trusted
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { execFile } from "node:child_process";
|
|
@@ -16,6 +16,7 @@ import { promisify } from "node:util";
|
|
|
16
16
|
import { getAgentDir, type ExtensionAPI, type ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
17
17
|
|
|
18
18
|
const execFileAsync = promisify(execFile);
|
|
19
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
19
20
|
|
|
20
21
|
interface BrrrConfig {
|
|
21
22
|
enabled: boolean;
|
|
@@ -40,6 +41,11 @@ interface BrrrPayload {
|
|
|
40
41
|
image_url?: string;
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
type ProjectConfigContext = {
|
|
45
|
+
cwd: string;
|
|
46
|
+
isProjectTrusted?: () => boolean;
|
|
47
|
+
};
|
|
48
|
+
|
|
43
49
|
const DEFAULT_CONFIG: BrrrConfig = {
|
|
44
50
|
enabled: true,
|
|
45
51
|
onlyWhenInteractive: true,
|
|
@@ -71,9 +77,15 @@ function mergeConfig(base: BrrrConfig, overrides: Partial<BrrrConfig>): BrrrConf
|
|
|
71
77
|
};
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
function
|
|
80
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
81
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function loadConfig(ctx: ProjectConfigContext): BrrrConfig {
|
|
75
85
|
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "brrr.json"));
|
|
76
|
-
const projectConfig =
|
|
86
|
+
const projectConfig = canReadProjectConfig(ctx)
|
|
87
|
+
? readConfigFile(join(ctx.cwd, CONFIG_DIR_NAME, "brrr.json"))
|
|
88
|
+
: {};
|
|
77
89
|
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
78
90
|
}
|
|
79
91
|
|
|
@@ -219,13 +231,13 @@ export default function brrrExtension(pi: ExtensionAPI) {
|
|
|
219
231
|
pi.registerCommand("brrr", {
|
|
220
232
|
description: "Show brrr notification status",
|
|
221
233
|
handler: async (_args, ctx) => {
|
|
222
|
-
const config = loadConfig(ctx
|
|
234
|
+
const config = loadConfig(ctx);
|
|
223
235
|
notify(ctx, describeConfig(config, resolveWebhook(config.webhook)));
|
|
224
236
|
},
|
|
225
237
|
});
|
|
226
238
|
|
|
227
239
|
pi.on("agent_end", async (event, ctx) => {
|
|
228
|
-
const config = loadConfig(ctx
|
|
240
|
+
const config = loadConfig(ctx);
|
|
229
241
|
if (!config.enabled) return;
|
|
230
242
|
if (config.onlyWhenInteractive && !ctx.hasUI) return;
|
|
231
243
|
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
type ExtensionContext,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
|
|
9
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
10
|
+
|
|
9
11
|
const EXTENSION_ID = "claude-fast";
|
|
10
12
|
const PROVIDER_ID = "anthropic";
|
|
11
13
|
const API_ID = "anthropic-messages";
|
|
@@ -35,6 +37,11 @@ type SessionState = {
|
|
|
35
37
|
lastInjectedModel?: string;
|
|
36
38
|
};
|
|
37
39
|
|
|
40
|
+
type ProjectConfigContext = {
|
|
41
|
+
cwd: string;
|
|
42
|
+
isProjectTrusted?: () => boolean;
|
|
43
|
+
};
|
|
44
|
+
|
|
38
45
|
type RecursivePartial<T> = {
|
|
39
46
|
[P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
|
40
47
|
};
|
|
@@ -77,21 +84,25 @@ function mergeConfig(
|
|
|
77
84
|
};
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
88
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
function findProjectConfigPath(cwd: string): string {
|
|
81
92
|
let current = cwd;
|
|
82
93
|
while (true) {
|
|
83
|
-
const candidate = join(current,
|
|
94
|
+
const candidate = join(current, CONFIG_DIR_NAME, "claude-fast.json");
|
|
84
95
|
if (existsSync(candidate)) return candidate;
|
|
85
96
|
|
|
86
97
|
const parent = dirname(current);
|
|
87
|
-
if (parent === current) return join(cwd,
|
|
98
|
+
if (parent === current) return join(cwd, CONFIG_DIR_NAME, "claude-fast.json");
|
|
88
99
|
current = parent;
|
|
89
100
|
}
|
|
90
101
|
}
|
|
91
102
|
|
|
92
|
-
function loadConfig(
|
|
103
|
+
function loadConfig(ctx: ProjectConfigContext): ClaudeFastConfig {
|
|
93
104
|
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "claude-fast.json"));
|
|
94
|
-
const projectConfig = readConfigFile(findProjectConfigPath(cwd));
|
|
105
|
+
const projectConfig = canReadProjectConfig(ctx) ? readConfigFile(findProjectConfigPath(ctx.cwd)) : {};
|
|
95
106
|
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
96
107
|
}
|
|
97
108
|
|
|
@@ -235,7 +246,7 @@ export default function claudeFastExtension(pi: ExtensionAPI) {
|
|
|
235
246
|
let state = states.get(ctx.sessionManager);
|
|
236
247
|
if (!state) {
|
|
237
248
|
state = {
|
|
238
|
-
config: loadConfig(ctx
|
|
249
|
+
config: loadConfig(ctx),
|
|
239
250
|
override: "auto",
|
|
240
251
|
};
|
|
241
252
|
states.set(ctx.sessionManager, state);
|
|
@@ -245,7 +256,7 @@ export default function claudeFastExtension(pi: ExtensionAPI) {
|
|
|
245
256
|
|
|
246
257
|
pi.on("session_start", (_event, ctx) => {
|
|
247
258
|
const state: SessionState = {
|
|
248
|
-
config: loadConfig(ctx
|
|
259
|
+
config: loadConfig(ctx),
|
|
249
260
|
override: "auto",
|
|
250
261
|
};
|
|
251
262
|
states.set(ctx.sessionManager, state);
|
|
@@ -12,6 +12,7 @@ It replaces pi's built-in footer with a cleaner two-line layout that focuses on
|
|
|
12
12
|
- red `DUMB ZONE` indicator when context usage is above 200k tokens
|
|
13
13
|
- current model and thinking level
|
|
14
14
|
- OpenAI Codex 5-hour and 7-day usage when available
|
|
15
|
+
- `xp` marker when Pi experimental features are enabled
|
|
15
16
|
|
|
16
17
|
## Layout
|
|
17
18
|
|
|
@@ -41,6 +42,12 @@ When using `openai-codex`, the bottom-left line also includes subscription usage
|
|
|
41
42
|
44.1% · 5h 12% · 7d 38%
|
|
42
43
|
```
|
|
43
44
|
|
|
45
|
+
When `PI_EXPERIMENTAL=1`, the bottom-left line also includes an experimental marker:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
44.1% · xp
|
|
49
|
+
```
|
|
50
|
+
|
|
44
51
|
On narrow terminals it falls back to one item per line.
|
|
45
52
|
|
|
46
53
|
## Install
|
|
@@ -76,6 +83,8 @@ Config files are merged, with project config overriding global config:
|
|
|
76
83
|
- `~/.pi/agent/extensions/minimal-footer.json`
|
|
77
84
|
- `<project>/.pi/minimal-footer.json`
|
|
78
85
|
|
|
86
|
+
Project config is only read after Pi reports that the project is trusted.
|
|
87
|
+
|
|
79
88
|
A ready-to-copy sample file is included at [`minimal-footer.example.json`](./minimal-footer.example.json).
|
|
80
89
|
|
|
81
90
|
Example:
|
|
@@ -105,6 +114,11 @@ Example:
|
|
|
105
114
|
"label": "7d"
|
|
106
115
|
}
|
|
107
116
|
}
|
|
117
|
+
},
|
|
118
|
+
"experimentalMarker": {
|
|
119
|
+
"enabled": true,
|
|
120
|
+
"label": "xp",
|
|
121
|
+
"color": "warning"
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
```
|
|
@@ -145,6 +159,16 @@ Disable one session-limit window:
|
|
|
145
159
|
}
|
|
146
160
|
```
|
|
147
161
|
|
|
162
|
+
Disable the experimental-features marker:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"experimentalMarker": {
|
|
167
|
+
"enabled": false
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
148
172
|
### Config fields
|
|
149
173
|
|
|
150
174
|
- `context.showPercent`: show the context percentage
|
|
@@ -159,6 +183,9 @@ Disable one session-limit window:
|
|
|
159
183
|
- `codexUsage.windows.primary.label`: label for the primary usage window
|
|
160
184
|
- `codexUsage.windows.secondary.enabled`: show the secondary usage window
|
|
161
185
|
- `codexUsage.windows.secondary.label`: label for the secondary usage window
|
|
186
|
+
- `experimentalMarker.enabled`: show the marker when `PI_EXPERIMENTAL=1`
|
|
187
|
+
- `experimentalMarker.label`: marker text
|
|
188
|
+
- `experimentalMarker.color`: theme color for the marker (`error`, `warning`, `accent`, `text`, or `dim`)
|
|
162
189
|
|
|
163
190
|
## What it shows
|
|
164
191
|
|
|
@@ -166,6 +193,7 @@ Disable one session-limit window:
|
|
|
166
193
|
- **Top right:** current repo directory name
|
|
167
194
|
- **Bottom left:** current context usage percentage, plus red `DUMB ZONE` above 200k context tokens
|
|
168
195
|
- **Bottom left on `openai-codex`:** current context usage percentage plus 5-hour and 7-day Codex usage
|
|
196
|
+
- **Bottom left with `PI_EXPERIMENTAL=1`:** current context usage percentage plus `xp`
|
|
169
197
|
- **Bottom right:** model id and thinking level
|
|
170
198
|
|
|
171
199
|
## Publishing notes
|
|
@@ -179,5 +207,6 @@ This extension also lives inside the broader [`pi-extensions`](../../README.md)
|
|
|
179
207
|
- Shows only context percentage, not context window size.
|
|
180
208
|
- Shows `DUMB ZONE` only while context usage is above 200k tokens.
|
|
181
209
|
- Shows the model id rather than a provider-specific display label.
|
|
210
|
+
- Shows `xp` when `PI_EXPERIMENTAL=1`.
|
|
182
211
|
- For `openai-codex`, reads pi's stored OAuth login and fetches usage from ChatGPT's backend usage endpoint.
|
|
183
212
|
- Usage is cached briefly in memory and refreshed after turns.
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
type UsageSnapshot,
|
|
15
15
|
} from "./openai-usage";
|
|
16
16
|
|
|
17
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
18
|
+
|
|
17
19
|
const DEFAULT_CONFIG: MinimalFooterConfig = {
|
|
18
20
|
context: {
|
|
19
21
|
showPercent: true,
|
|
@@ -39,6 +41,11 @@ const DEFAULT_CONFIG: MinimalFooterConfig = {
|
|
|
39
41
|
},
|
|
40
42
|
},
|
|
41
43
|
},
|
|
44
|
+
experimentalMarker: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
label: "xp",
|
|
47
|
+
color: "warning",
|
|
48
|
+
},
|
|
42
49
|
};
|
|
43
50
|
|
|
44
51
|
const DUMB_ZONE_COLORS = new Set<DumbZoneColor>([
|
|
@@ -80,6 +87,11 @@ interface MinimalFooterConfig {
|
|
|
80
87
|
};
|
|
81
88
|
};
|
|
82
89
|
};
|
|
90
|
+
experimentalMarker: {
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
label: string;
|
|
93
|
+
color: DumbZoneColor;
|
|
94
|
+
};
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
type UsageSessionState = {
|
|
@@ -93,6 +105,11 @@ type UsageSessionState = {
|
|
|
93
105
|
requestRender?: () => void;
|
|
94
106
|
};
|
|
95
107
|
|
|
108
|
+
type ProjectConfigContext = {
|
|
109
|
+
cwd: string;
|
|
110
|
+
isProjectTrusted?: () => boolean;
|
|
111
|
+
};
|
|
112
|
+
|
|
96
113
|
function readConfigFile(path: string): RecursivePartial<MinimalFooterConfig> {
|
|
97
114
|
if (!existsSync(path)) return {};
|
|
98
115
|
|
|
@@ -113,6 +130,7 @@ function mergeConfig(
|
|
|
113
130
|
const codexUsage = overrides.codexUsage;
|
|
114
131
|
const primaryWindow = codexUsage?.windows?.primary;
|
|
115
132
|
const secondaryWindow = codexUsage?.windows?.secondary;
|
|
133
|
+
const experimentalMarker = overrides.experimentalMarker;
|
|
116
134
|
|
|
117
135
|
return {
|
|
118
136
|
context: {
|
|
@@ -154,6 +172,17 @@ function mergeConfig(
|
|
|
154
172
|
},
|
|
155
173
|
},
|
|
156
174
|
},
|
|
175
|
+
experimentalMarker: {
|
|
176
|
+
enabled: normalizeBoolean(
|
|
177
|
+
experimentalMarker?.enabled,
|
|
178
|
+
base.experimentalMarker.enabled,
|
|
179
|
+
),
|
|
180
|
+
label: normalizeLabel(experimentalMarker?.label, base.experimentalMarker.label),
|
|
181
|
+
color: normalizeDumbZoneColor(
|
|
182
|
+
experimentalMarker?.color,
|
|
183
|
+
base.experimentalMarker.color,
|
|
184
|
+
),
|
|
185
|
+
},
|
|
157
186
|
};
|
|
158
187
|
}
|
|
159
188
|
|
|
@@ -177,21 +206,25 @@ function normalizeDumbZoneColor(value: unknown, fallback: DumbZoneColor): DumbZo
|
|
|
177
206
|
return DUMB_ZONE_COLORS.has(value as DumbZoneColor) ? (value as DumbZoneColor) : fallback;
|
|
178
207
|
}
|
|
179
208
|
|
|
209
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
210
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
211
|
+
}
|
|
212
|
+
|
|
180
213
|
function findProjectConfigPath(cwd: string): string {
|
|
181
214
|
let current = cwd;
|
|
182
215
|
while (true) {
|
|
183
|
-
const candidate = join(current,
|
|
216
|
+
const candidate = join(current, CONFIG_DIR_NAME, "minimal-footer.json");
|
|
184
217
|
if (existsSync(candidate)) return candidate;
|
|
185
218
|
|
|
186
219
|
const parent = dirname(current);
|
|
187
|
-
if (parent === current) return join(cwd,
|
|
220
|
+
if (parent === current) return join(cwd, CONFIG_DIR_NAME, "minimal-footer.json");
|
|
188
221
|
current = parent;
|
|
189
222
|
}
|
|
190
223
|
}
|
|
191
224
|
|
|
192
|
-
function loadConfig(
|
|
225
|
+
function loadConfig(ctx: ProjectConfigContext): MinimalFooterConfig {
|
|
193
226
|
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "minimal-footer.json"));
|
|
194
|
-
const projectConfig = readConfigFile(findProjectConfigPath(cwd));
|
|
227
|
+
const projectConfig = canReadProjectConfig(ctx) ? readConfigFile(findProjectConfigPath(ctx.cwd)) : {};
|
|
195
228
|
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
196
229
|
}
|
|
197
230
|
|
|
@@ -202,6 +235,10 @@ function shouldShowCodexUsage(config: MinimalFooterConfig): boolean {
|
|
|
202
235
|
);
|
|
203
236
|
}
|
|
204
237
|
|
|
238
|
+
function shouldShowExperimentalMarker(config: MinimalFooterConfig): boolean {
|
|
239
|
+
return config.experimentalMarker.enabled && process.env.PI_EXPERIMENTAL === "1";
|
|
240
|
+
}
|
|
241
|
+
|
|
205
242
|
function clearUsageState(state: UsageSessionState): void {
|
|
206
243
|
state.snapshot = undefined;
|
|
207
244
|
state.lastFetchedAt = undefined;
|
|
@@ -268,7 +305,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
268
305
|
pi.on("session_start", (_event, ctx) => {
|
|
269
306
|
const state: UsageSessionState = {
|
|
270
307
|
authStorage: AuthStorage.create(),
|
|
271
|
-
config: loadConfig(ctx
|
|
308
|
+
config: loadConfig(ctx),
|
|
272
309
|
loading: false,
|
|
273
310
|
};
|
|
274
311
|
states.set(ctx.sessionManager, state);
|
|
@@ -306,6 +343,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
306
343
|
if (state.config.context.showPercent) contextParts.push(theme.fg("dim", context));
|
|
307
344
|
if (inDumbZone) contextParts.push(theme.fg(dumbZone.color, dumbZone.label));
|
|
308
345
|
if (usageSummary) contextParts.push(theme.fg("dim", usageSummary));
|
|
346
|
+
if (shouldShowExperimentalMarker(state.config)) {
|
|
347
|
+
const marker = state.config.experimentalMarker;
|
|
348
|
+
contextParts.push(theme.fg(marker.color, marker.label));
|
|
349
|
+
}
|
|
309
350
|
const contextStyled = contextParts.join(theme.fg("dim", " · "));
|
|
310
351
|
const modelStyled = theme.fg("dim", modelText);
|
|
311
352
|
|
|
@@ -67,6 +67,8 @@ Config files are merged, with project config overriding global config:
|
|
|
67
67
|
- `~/.pi/agent/extensions/notify.json`
|
|
68
68
|
- `<project>/.pi/notify.json`
|
|
69
69
|
|
|
70
|
+
Project config is only read after Pi reports that the project is trusted.
|
|
71
|
+
|
|
70
72
|
A ready-to-copy sample file is included at [`notify.example.json`](./notify.example.json).
|
|
71
73
|
|
|
72
74
|
Example:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Config files (project overrides global):
|
|
12
12
|
* - ~/.pi/agent/extensions/notify.json
|
|
13
|
-
* - <cwd>/.pi/notify.json
|
|
13
|
+
* - <cwd>/.pi/notify.json, when the project is trusted
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { execFile } from "node:child_process";
|
|
@@ -19,10 +19,17 @@ import { join } from "node:path";
|
|
|
19
19
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
21
21
|
|
|
22
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
23
|
+
|
|
22
24
|
type TerminalBackend = "auto" | "osc777" | "osc99" | "none";
|
|
23
25
|
type DesktopBackend = "auto" | "macos" | "linux" | "windows-toast" | "none";
|
|
24
26
|
type SoundBackend = "auto" | "macos" | "linux" | "windows-beep" | "command" | "none";
|
|
25
27
|
|
|
28
|
+
type ProjectConfigContext = {
|
|
29
|
+
cwd: string;
|
|
30
|
+
isProjectTrusted?: () => boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
26
33
|
interface NotifyConfig {
|
|
27
34
|
enabled: boolean;
|
|
28
35
|
onlyWhenInteractive: boolean;
|
|
@@ -111,9 +118,15 @@ function mergeConfig(base: NotifyConfig, overrides: Partial<NotifyConfig>): Noti
|
|
|
111
118
|
};
|
|
112
119
|
}
|
|
113
120
|
|
|
114
|
-
function
|
|
121
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
122
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function loadConfig(ctx: ProjectConfigContext): NotifyConfig {
|
|
115
126
|
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "notify.json"));
|
|
116
|
-
const projectConfig =
|
|
127
|
+
const projectConfig = canReadProjectConfig(ctx)
|
|
128
|
+
? readConfigFile(join(ctx.cwd, CONFIG_DIR_NAME, "notify.json"))
|
|
129
|
+
: {};
|
|
117
130
|
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
118
131
|
}
|
|
119
132
|
|
|
@@ -245,7 +258,7 @@ async function playSound(config: NotifyConfig, backend: Exclude<SoundBackend, "a
|
|
|
245
258
|
|
|
246
259
|
export default function notifyExtension(pi: ExtensionAPI) {
|
|
247
260
|
pi.on("agent_end", async (_event, ctx) => {
|
|
248
|
-
const config = loadConfig(ctx
|
|
261
|
+
const config = loadConfig(ctx);
|
|
249
262
|
if (!config.enabled) return;
|
|
250
263
|
if (config.onlyWhenInteractive && !ctx.hasUI) return;
|
|
251
264
|
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
type ExtensionContext,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
|
|
9
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
10
|
+
|
|
9
11
|
const EXTENSION_ID = "openai-fast";
|
|
10
12
|
const PROVIDER_ID = "openai-codex";
|
|
11
13
|
const API_ID = "openai-codex-responses";
|
|
@@ -33,6 +35,11 @@ type SessionState = {
|
|
|
33
35
|
lastInjectedModel?: string;
|
|
34
36
|
};
|
|
35
37
|
|
|
38
|
+
type ProjectConfigContext = {
|
|
39
|
+
cwd: string;
|
|
40
|
+
isProjectTrusted?: () => boolean;
|
|
41
|
+
};
|
|
42
|
+
|
|
36
43
|
type RecursivePartial<T> = {
|
|
37
44
|
[P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
|
|
38
45
|
};
|
|
@@ -71,21 +78,25 @@ function mergeConfig(
|
|
|
71
78
|
};
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
82
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
83
|
+
}
|
|
84
|
+
|
|
74
85
|
function findProjectConfigPath(cwd: string): string {
|
|
75
86
|
let current = cwd;
|
|
76
87
|
while (true) {
|
|
77
|
-
const candidate = join(current,
|
|
88
|
+
const candidate = join(current, CONFIG_DIR_NAME, "openai-fast.json");
|
|
78
89
|
if (existsSync(candidate)) return candidate;
|
|
79
90
|
|
|
80
91
|
const parent = dirname(current);
|
|
81
|
-
if (parent === current) return join(cwd,
|
|
92
|
+
if (parent === current) return join(cwd, CONFIG_DIR_NAME, "openai-fast.json");
|
|
82
93
|
current = parent;
|
|
83
94
|
}
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
function loadConfig(
|
|
97
|
+
function loadConfig(ctx: ProjectConfigContext): OpenAIFastConfig {
|
|
87
98
|
const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "openai-fast.json"));
|
|
88
|
-
const projectConfig = readConfigFile(findProjectConfigPath(cwd));
|
|
99
|
+
const projectConfig = canReadProjectConfig(ctx) ? readConfigFile(findProjectConfigPath(ctx.cwd)) : {};
|
|
89
100
|
return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
|
|
90
101
|
}
|
|
91
102
|
|
|
@@ -211,7 +222,7 @@ export default function openAIFastExtension(pi: ExtensionAPI) {
|
|
|
211
222
|
let state = states.get(ctx.sessionManager);
|
|
212
223
|
if (!state) {
|
|
213
224
|
state = {
|
|
214
|
-
config: loadConfig(ctx
|
|
225
|
+
config: loadConfig(ctx),
|
|
215
226
|
override: "auto",
|
|
216
227
|
};
|
|
217
228
|
states.set(ctx.sessionManager, state);
|
|
@@ -221,7 +232,7 @@ export default function openAIFastExtension(pi: ExtensionAPI) {
|
|
|
221
232
|
|
|
222
233
|
pi.on("session_start", (_event, ctx) => {
|
|
223
234
|
const state: SessionState = {
|
|
224
|
-
config: loadConfig(ctx
|
|
235
|
+
config: loadConfig(ctx),
|
|
225
236
|
override: "auto",
|
|
226
237
|
};
|
|
227
238
|
states.set(ctx.sessionManager, state);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-openai-fast",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "A pi extension that enables OpenAI Codex Fast mode for ChatGPT-auth GPT-5.4 and GPT-5.5 by injecting the priority service tier.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -31,7 +31,7 @@ Then reload pi:
|
|
|
31
31
|
|
|
32
32
|
- Behavior is intentionally kept equivalent to the upstream source, with only packaging/attribution changes for this repository.
|
|
33
33
|
- PR review requires `gh` access and a clean working tree for tracked files.
|
|
34
|
-
- If a `REVIEW_GUIDELINES.md` file exists next to the repo's `.pi` directory, its contents are appended to the review prompt.
|
|
34
|
+
- If the project is trusted and a `REVIEW_GUIDELINES.md` file exists next to the repo's `.pi` directory, its contents are appended to the review prompt.
|
|
35
35
|
|
|
36
36
|
## License and attribution
|
|
37
37
|
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
* - `/review --extra "focus on performance regressions"` - add extra review instruction (works with any mode)
|
|
30
30
|
*
|
|
31
31
|
* Project-specific review guidelines:
|
|
32
|
-
* - If a REVIEW_GUIDELINES.md file exists in the
|
|
33
|
-
* its contents are appended to the review prompt.
|
|
32
|
+
* - If the project is trusted and a REVIEW_GUIDELINES.md file exists in the
|
|
33
|
+
* same directory as .pi, its contents are appended to the review prompt.
|
|
34
34
|
*
|
|
35
35
|
* Note: PR review requires a clean working tree (no uncommitted changes to tracked files).
|
|
36
36
|
*/
|
|
@@ -49,6 +49,8 @@ import {
|
|
|
49
49
|
import path from "node:path";
|
|
50
50
|
import { promises as fs } from "node:fs";
|
|
51
51
|
|
|
52
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
53
|
+
|
|
52
54
|
// State to track fresh session review (where we branched from).
|
|
53
55
|
// Module-level state means only one review can be active at a time.
|
|
54
56
|
// This is intentional - the UI and /end-review command assume a single active review.
|
|
@@ -75,6 +77,15 @@ type ReviewSettingsState = {
|
|
|
75
77
|
customInstructions?: string;
|
|
76
78
|
};
|
|
77
79
|
|
|
80
|
+
type ProjectConfigContext = {
|
|
81
|
+
cwd: string;
|
|
82
|
+
isProjectTrusted?: () => boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
|
|
86
|
+
return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
function setReviewWidget(ctx: ExtensionContext, active: boolean) {
|
|
79
90
|
if (!ctx.hasUI) return;
|
|
80
91
|
if (!active) {
|
|
@@ -501,7 +512,7 @@ async function loadProjectReviewGuidelines(cwd: string): Promise<string | null>
|
|
|
501
512
|
let currentDir = path.resolve(cwd);
|
|
502
513
|
|
|
503
514
|
while (true) {
|
|
504
|
-
const piDir = path.join(currentDir,
|
|
515
|
+
const piDir = path.join(currentDir, CONFIG_DIR_NAME);
|
|
505
516
|
const guidelinesPath = path.join(currentDir, "REVIEW_GUIDELINES.md");
|
|
506
517
|
|
|
507
518
|
const piStats = await fs.stat(piDir).catch(() => null);
|
|
@@ -1420,7 +1431,7 @@ export default function reviewExtension(pi: ExtensionAPI) {
|
|
|
1420
1431
|
includeLocalChanges: options?.includeLocalChanges === true,
|
|
1421
1432
|
});
|
|
1422
1433
|
const hint = getUserFacingHint(target);
|
|
1423
|
-
const projectGuidelines = await loadProjectReviewGuidelines(ctx.cwd);
|
|
1434
|
+
const projectGuidelines = canReadProjectConfig(ctx) ? await loadProjectReviewGuidelines(ctx.cwd) : null;
|
|
1424
1435
|
|
|
1425
1436
|
// Combine the review rubric with the specific prompt
|
|
1426
1437
|
let fullPrompt = `${REVIEW_RUBRIC}\n\n---\n\nPlease perform a code review with the following focus:\n\n${prompt}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.48",
|
|
4
4
|
"description": "A collection of pi extensions and skills for annotation UIs, context management, workflow audits, review-comment triage, notifications, brrr push alerts, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, model/provider helpers, and Xiaohei-style article illustrations.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|