@diegopetrucci/pi-extensions 0.1.50 → 0.1.51
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/.pi-fleet-tested-version +1 -1
- package/README.md +1 -1
- package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/brrr/.pi-fleet-tested-version +1 -1
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
- package/extensions/claude-fast/package.json +1 -1
- package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/.pi-fleet-tested-version +1 -1
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/git-footer/.pi-fleet-tested-version +1 -1
- package/extensions/git-footer/package.json +1 -1
- package/extensions/gnosis/.pi-fleet-tested-version +1 -1
- package/extensions/gnosis/package.json +1 -1
- package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
- package/extensions/illustrations-to-explain-things/package.json +1 -1
- package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/.pi-fleet-tested-version +1 -1
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
- package/extensions/minimal-footer/README.md +32 -3
- package/extensions/minimal-footer/index.ts +539 -2
- package/extensions/minimal-footer/minimal-footer.example.json +6 -0
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/.pi-fleet-tested-version +1 -1
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
- package/extensions/openai-fast/package.json +1 -1
- package/extensions/oracle/.pi-fleet-tested-version +1 -1
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/package.json +1 -1
- package/extensions/review/.pi-fleet-tested-version +1 -1
- package/extensions/review/package.json +1 -1
- package/extensions/todo/.pi-fleet-tested-version +1 -1
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +1 -1
package/.pi-fleet-tested-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
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`, optional `xp` marker, 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 plus cached git dirty/ahead/PR status and 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.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-annotate-git-diff",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A standalone pi extension that adds /annotate-git-diff, a native Glimpse UI for annotating git diffs and sending feedback to the editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-annotate-last-message",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A standalone pi extension that adds /annotate-last-message, a native Glimpse UI for annotating the latest assistant reply.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-illustrations-to-explain-things",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A pi skill for generating clean, absurd Xiaohei-style article illustrations, shot lists, and visual metaphors.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -7,6 +7,7 @@ A minimal custom footer for pi.
|
|
|
7
7
|
It replaces pi's built-in footer with a cleaner two-line layout that focuses on the information I care about most:
|
|
8
8
|
|
|
9
9
|
- current git branch
|
|
10
|
+
- TLH-style git dirty counts, ahead/behind counts, and optional PR number
|
|
10
11
|
- current repo name
|
|
11
12
|
- current context percentage
|
|
12
13
|
- red `DUMB ZONE` indicator when context usage is above 200k tokens
|
|
@@ -19,7 +20,7 @@ It replaces pi's built-in footer with a cleaner two-line layout that focuses on
|
|
|
19
20
|
On wide terminals it renders two lines:
|
|
20
21
|
|
|
21
22
|
```text
|
|
22
|
-
<git-branch>
|
|
23
|
+
<git-branch> · <git-status> <repo-name>
|
|
23
24
|
<context-%> <model> <thinking>
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -30,6 +31,14 @@ fix/remove-detached-image-tasks SendItToMy
|
|
|
30
31
|
44.1% gpt-5.4 high
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
When the project is trusted and the repo has local or remote git state, the top-left line includes the same cached summary as `git-footer`:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
main · +1 ~2 ?3 ↑1 • PR #42
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The markers are `!` conflicts, `+` staged, `~` unstaged, `?` untracked, `↑` ahead, and `↓` behind. PR numbers are fetched best-effort with `gh pr view`. Git polling is skipped until Pi reports that the project is trusted, and `git status` is run with fsmonitor disabled.
|
|
41
|
+
|
|
33
42
|
When context usage is above 200k tokens, the bottom-left line includes a red warning:
|
|
34
43
|
|
|
35
44
|
```text
|
|
@@ -119,6 +128,12 @@ Example:
|
|
|
119
128
|
"enabled": true,
|
|
120
129
|
"label": "xp",
|
|
121
130
|
"color": "warning"
|
|
131
|
+
},
|
|
132
|
+
"gitStatus": {
|
|
133
|
+
"enabled": true,
|
|
134
|
+
"refreshIntervalMs": 8000,
|
|
135
|
+
"gitTimeoutMs": 1500,
|
|
136
|
+
"ghTimeoutMs": 3000
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
```
|
|
@@ -169,6 +184,16 @@ Disable the experimental-features marker:
|
|
|
169
184
|
}
|
|
170
185
|
```
|
|
171
186
|
|
|
187
|
+
Disable git dirty/ahead/PR status:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"gitStatus": {
|
|
192
|
+
"enabled": false
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
172
197
|
### Config fields
|
|
173
198
|
|
|
174
199
|
- `context.showPercent`: show the context percentage
|
|
@@ -186,10 +211,14 @@ Disable the experimental-features marker:
|
|
|
186
211
|
- `experimentalMarker.enabled`: show the marker when `PI_EXPERIMENTAL=1`
|
|
187
212
|
- `experimentalMarker.label`: marker text
|
|
188
213
|
- `experimentalMarker.color`: theme color for the marker (`error`, `warning`, `accent`, `text`, or `dim`)
|
|
214
|
+
- `gitStatus.enabled`: show cached git dirty counts, ahead/behind counts, and optional PR number for trusted projects
|
|
215
|
+
- `gitStatus.refreshIntervalMs`: background git status refresh interval
|
|
216
|
+
- `gitStatus.gitTimeoutMs`: timeout for `git status --porcelain=v2 --branch`
|
|
217
|
+
- `gitStatus.ghTimeoutMs`: timeout for best-effort `gh pr view`
|
|
189
218
|
|
|
190
219
|
## What it shows
|
|
191
220
|
|
|
192
|
-
- **Top left:** current git branch
|
|
221
|
+
- **Top left:** current git branch plus dirty/ahead/PR status when available
|
|
193
222
|
- **Top right:** current repo directory name
|
|
194
223
|
- **Bottom left:** current context usage percentage, plus red `DUMB ZONE` above 200k context tokens
|
|
195
224
|
- **Bottom left on `openai-codex`:** current context usage percentage plus 5-hour and 7-day Codex usage
|
|
@@ -203,7 +232,7 @@ This extension also lives inside the broader [`pi-extensions`](../../README.md)
|
|
|
203
232
|
## Notes
|
|
204
233
|
|
|
205
234
|
- Replaces pi's built-in footer entirely.
|
|
206
|
-
- Uses pi footer data for git branch updates.
|
|
235
|
+
- Uses pi footer data for git branch updates, plus background cached git/gh checks for dirty counts and PR number after project trust is granted.
|
|
207
236
|
- Shows only context percentage, not context window size.
|
|
208
237
|
- Shows `DUMB ZONE` only while context usage is above 200k tokens.
|
|
209
238
|
- Shows the model id rather than a provider-specific display label.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
2
3
|
import { basename, dirname, join } from "node:path";
|
|
3
4
|
import {
|
|
4
5
|
AuthStorage,
|
|
@@ -46,6 +47,12 @@ const DEFAULT_CONFIG: MinimalFooterConfig = {
|
|
|
46
47
|
label: "xp",
|
|
47
48
|
color: "warning",
|
|
48
49
|
},
|
|
50
|
+
gitStatus: {
|
|
51
|
+
enabled: true,
|
|
52
|
+
refreshIntervalMs: 8_000,
|
|
53
|
+
gitTimeoutMs: 1_500,
|
|
54
|
+
ghTimeoutMs: 3_000,
|
|
55
|
+
},
|
|
49
56
|
};
|
|
50
57
|
|
|
51
58
|
const DUMB_ZONE_COLORS = new Set<DumbZoneColor>([
|
|
@@ -92,6 +99,12 @@ interface MinimalFooterConfig {
|
|
|
92
99
|
label: string;
|
|
93
100
|
color: DumbZoneColor;
|
|
94
101
|
};
|
|
102
|
+
gitStatus: {
|
|
103
|
+
enabled: boolean;
|
|
104
|
+
refreshIntervalMs: number;
|
|
105
|
+
gitTimeoutMs: number;
|
|
106
|
+
ghTimeoutMs: number;
|
|
107
|
+
};
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
type UsageSessionState = {
|
|
@@ -103,6 +116,7 @@ type UsageSessionState = {
|
|
|
103
116
|
error?: string;
|
|
104
117
|
inflight?: Promise<void>;
|
|
105
118
|
requestRender?: () => void;
|
|
119
|
+
gitCache?: GitFooterCache;
|
|
106
120
|
};
|
|
107
121
|
|
|
108
122
|
type ProjectConfigContext = {
|
|
@@ -110,6 +124,67 @@ type ProjectConfigContext = {
|
|
|
110
124
|
isProjectTrusted?: () => boolean;
|
|
111
125
|
};
|
|
112
126
|
|
|
127
|
+
type GitStatusSnapshot = {
|
|
128
|
+
branch?: string;
|
|
129
|
+
staged: number;
|
|
130
|
+
unstaged: number;
|
|
131
|
+
untracked: number;
|
|
132
|
+
conflict: number;
|
|
133
|
+
ahead: number;
|
|
134
|
+
behind: number;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
type PullRequestSnapshot = {
|
|
138
|
+
number?: number | string;
|
|
139
|
+
state?: string;
|
|
140
|
+
isDraft?: boolean;
|
|
141
|
+
url?: string;
|
|
142
|
+
title?: string;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
type CommandResult = {
|
|
146
|
+
stdout: string;
|
|
147
|
+
stderr: string;
|
|
148
|
+
exitCode: number | null;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
type CommandRunner = (
|
|
152
|
+
command: string,
|
|
153
|
+
args: readonly string[],
|
|
154
|
+
options: { cwd: string; signal: AbortSignal },
|
|
155
|
+
) => Promise<CommandResult>;
|
|
156
|
+
|
|
157
|
+
type TimerHandle = unknown;
|
|
158
|
+
|
|
159
|
+
type Clock = {
|
|
160
|
+
setInterval(callback: () => void, ms: number): TimerHandle;
|
|
161
|
+
clearInterval(handle: TimerHandle): void;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
type GitFooterCacheOptions = {
|
|
165
|
+
cwd: () => string;
|
|
166
|
+
canRun?: () => boolean;
|
|
167
|
+
runner?: CommandRunner;
|
|
168
|
+
clock?: Clock;
|
|
169
|
+
refreshIntervalMs?: number;
|
|
170
|
+
gitTimeoutMs?: number;
|
|
171
|
+
ghTimeoutMs?: number;
|
|
172
|
+
onChange?: () => void;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const BRANCH_HEAD_PREFIX = "# branch.head ";
|
|
176
|
+
const BRANCH_AB_PREFIX = "# branch.ab ";
|
|
177
|
+
const STATUS_SEPARATOR = " • ";
|
|
178
|
+
const GIT_STATUS_ARGS = [
|
|
179
|
+
"--no-optional-locks",
|
|
180
|
+
"-c",
|
|
181
|
+
"core.fsmonitor=false",
|
|
182
|
+
"status",
|
|
183
|
+
"--porcelain=v2",
|
|
184
|
+
"--branch",
|
|
185
|
+
] as const;
|
|
186
|
+
const GH_PR_VIEW_ARGS = ["pr", "view", "--json", "number,state,isDraft,url,title"] as const;
|
|
187
|
+
|
|
113
188
|
function readConfigFile(path: string): RecursivePartial<MinimalFooterConfig> {
|
|
114
189
|
if (!existsSync(path)) return {};
|
|
115
190
|
|
|
@@ -131,6 +206,7 @@ function mergeConfig(
|
|
|
131
206
|
const primaryWindow = codexUsage?.windows?.primary;
|
|
132
207
|
const secondaryWindow = codexUsage?.windows?.secondary;
|
|
133
208
|
const experimentalMarker = overrides.experimentalMarker;
|
|
209
|
+
const gitStatus = overrides.gitStatus;
|
|
134
210
|
|
|
135
211
|
return {
|
|
136
212
|
context: {
|
|
@@ -183,6 +259,21 @@ function mergeConfig(
|
|
|
183
259
|
base.experimentalMarker.color,
|
|
184
260
|
),
|
|
185
261
|
},
|
|
262
|
+
gitStatus: {
|
|
263
|
+
enabled: normalizeBoolean(gitStatus?.enabled, base.gitStatus.enabled),
|
|
264
|
+
refreshIntervalMs: normalizePositiveNumber(
|
|
265
|
+
gitStatus?.refreshIntervalMs,
|
|
266
|
+
base.gitStatus.refreshIntervalMs,
|
|
267
|
+
),
|
|
268
|
+
gitTimeoutMs: normalizePositiveNumber(
|
|
269
|
+
gitStatus?.gitTimeoutMs,
|
|
270
|
+
base.gitStatus.gitTimeoutMs,
|
|
271
|
+
),
|
|
272
|
+
ghTimeoutMs: normalizePositiveNumber(
|
|
273
|
+
gitStatus?.ghTimeoutMs,
|
|
274
|
+
base.gitStatus.ghTimeoutMs,
|
|
275
|
+
),
|
|
276
|
+
},
|
|
186
277
|
};
|
|
187
278
|
}
|
|
188
279
|
|
|
@@ -239,6 +330,410 @@ function shouldShowExperimentalMarker(config: MinimalFooterConfig): boolean {
|
|
|
239
330
|
return config.experimentalMarker.enabled && process.env.PI_EXPERIMENTAL === "1";
|
|
240
331
|
}
|
|
241
332
|
|
|
333
|
+
function shouldShowGitStatus(config: MinimalFooterConfig): boolean {
|
|
334
|
+
return config.gitStatus.enabled;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function canRunProjectGit(ctx: ProjectConfigContext): boolean {
|
|
338
|
+
return canReadProjectConfig(ctx);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function createEmptyGitStatus(): GitStatusSnapshot {
|
|
342
|
+
return {
|
|
343
|
+
branch: undefined,
|
|
344
|
+
staged: 0,
|
|
345
|
+
unstaged: 0,
|
|
346
|
+
untracked: 0,
|
|
347
|
+
conflict: 0,
|
|
348
|
+
ahead: 0,
|
|
349
|
+
behind: 0,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function positiveCount(value: number): number {
|
|
354
|
+
return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function addTrackedStatusCounts(status: GitStatusSnapshot, xy: string): void {
|
|
358
|
+
if (xy.length !== 2) return;
|
|
359
|
+
if (xy[0] !== ".") status.staged += 1;
|
|
360
|
+
if (xy[1] !== ".") status.unstaged += 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function parseBranchAheadBehind(line: string, status: GitStatusSnapshot): void {
|
|
364
|
+
const match = /^# branch\.ab \+(\d+) -(\d+)$/.exec(line);
|
|
365
|
+
if (!match) return;
|
|
366
|
+
status.ahead = Number.parseInt(match[1]!, 10);
|
|
367
|
+
status.behind = Number.parseInt(match[2]!, 10);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function normalizeBranchHead(value: string): string {
|
|
371
|
+
const branch = value.trim();
|
|
372
|
+
return branch === "(detached)" ? "detached" : branch;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function parseGitStatusPorcelainV2(output: string): GitStatusSnapshot {
|
|
376
|
+
const status = createEmptyGitStatus();
|
|
377
|
+
|
|
378
|
+
for (const rawLine of output.split("\n")) {
|
|
379
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
380
|
+
if (!line) continue;
|
|
381
|
+
|
|
382
|
+
if (line.startsWith(BRANCH_HEAD_PREFIX)) {
|
|
383
|
+
status.branch = normalizeBranchHead(line.slice(BRANCH_HEAD_PREFIX.length)) || undefined;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (line.startsWith(BRANCH_AB_PREFIX)) {
|
|
388
|
+
parseBranchAheadBehind(line, status);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (line.startsWith("1 ") || line.startsWith("2 ")) {
|
|
393
|
+
addTrackedStatusCounts(status, line.slice(2, 4));
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (line.startsWith("u ")) {
|
|
398
|
+
status.conflict += 1;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (line.startsWith("? ")) status.untracked += 1;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return status;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function formatGitStatusFooterSegment(status: GitStatusSnapshot | undefined): string | undefined {
|
|
409
|
+
if (!status) return undefined;
|
|
410
|
+
|
|
411
|
+
const parts: string[] = [];
|
|
412
|
+
const indicators: Array<[string, number]> = [
|
|
413
|
+
["!", positiveCount(status.conflict)],
|
|
414
|
+
["+", positiveCount(status.staged)],
|
|
415
|
+
["~", positiveCount(status.unstaged)],
|
|
416
|
+
["?", positiveCount(status.untracked)],
|
|
417
|
+
["↑", positiveCount(status.ahead)],
|
|
418
|
+
["↓", positiveCount(status.behind)],
|
|
419
|
+
];
|
|
420
|
+
|
|
421
|
+
for (const [prefix, count] of indicators) {
|
|
422
|
+
if (count > 0) parts.push(`${prefix}${count}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return parts.length > 0 ? parts.join(" ") : undefined;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function formatPullRequestFooterSegment(pullRequest: PullRequestSnapshot | undefined): string | undefined {
|
|
429
|
+
const value = pullRequest?.number;
|
|
430
|
+
if (Number.isSafeInteger(value) && Number(value) > 0) return `PR #${value}`;
|
|
431
|
+
if (typeof value === "string") {
|
|
432
|
+
const trimmed = value.trim();
|
|
433
|
+
if (/^[1-9]\d*$/.test(trimmed)) return `PR #${trimmed}`;
|
|
434
|
+
}
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function formatGitFooterStatus(
|
|
439
|
+
status: GitStatusSnapshot | undefined,
|
|
440
|
+
pullRequest: PullRequestSnapshot | undefined,
|
|
441
|
+
): string | undefined {
|
|
442
|
+
const parts = [
|
|
443
|
+
formatGitStatusFooterSegment(status),
|
|
444
|
+
formatPullRequestFooterSegment(pullRequest),
|
|
445
|
+
].filter((part): part is string => !!part);
|
|
446
|
+
return parts.length > 0 ? parts.join(STATUS_SEPARATOR) : undefined;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function parsePullRequestJson(stdout: string): PullRequestSnapshot | undefined {
|
|
450
|
+
const trimmed = stdout.trim();
|
|
451
|
+
if (!trimmed) return undefined;
|
|
452
|
+
let parsed: unknown;
|
|
453
|
+
try {
|
|
454
|
+
parsed = JSON.parse(trimmed);
|
|
455
|
+
} catch {
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
459
|
+
|
|
460
|
+
const record = parsed as Record<string, unknown>;
|
|
461
|
+
const snapshot: PullRequestSnapshot = {};
|
|
462
|
+
if (typeof record.number === "number" || typeof record.number === "string") {
|
|
463
|
+
snapshot.number = record.number;
|
|
464
|
+
}
|
|
465
|
+
if (typeof record.state === "string") snapshot.state = record.state;
|
|
466
|
+
if (typeof record.isDraft === "boolean") snapshot.isDraft = record.isDraft;
|
|
467
|
+
if (typeof record.url === "string") snapshot.url = record.url;
|
|
468
|
+
if (typeof record.title === "string") snapshot.title = record.title;
|
|
469
|
+
return snapshot;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function gitStatusSnapshotsEqual(
|
|
473
|
+
left: GitStatusSnapshot | undefined,
|
|
474
|
+
right: GitStatusSnapshot | undefined,
|
|
475
|
+
): boolean {
|
|
476
|
+
return (
|
|
477
|
+
left?.branch === right?.branch
|
|
478
|
+
&& left?.staged === right?.staged
|
|
479
|
+
&& left?.unstaged === right?.unstaged
|
|
480
|
+
&& left?.untracked === right?.untracked
|
|
481
|
+
&& left?.conflict === right?.conflict
|
|
482
|
+
&& left?.ahead === right?.ahead
|
|
483
|
+
&& left?.behind === right?.behind
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function pullRequestSnapshotsEqual(
|
|
488
|
+
left: PullRequestSnapshot | undefined,
|
|
489
|
+
right: PullRequestSnapshot | undefined,
|
|
490
|
+
): boolean {
|
|
491
|
+
return (
|
|
492
|
+
left?.number === right?.number
|
|
493
|
+
&& left?.state === right?.state
|
|
494
|
+
&& left?.isDraft === right?.isDraft
|
|
495
|
+
&& left?.url === right?.url
|
|
496
|
+
&& left?.title === right?.title
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function defaultRunner(
|
|
501
|
+
command: string,
|
|
502
|
+
args: readonly string[],
|
|
503
|
+
options: { cwd: string; signal: AbortSignal },
|
|
504
|
+
): Promise<CommandResult> {
|
|
505
|
+
return new Promise((resolve, reject) => {
|
|
506
|
+
let child;
|
|
507
|
+
try {
|
|
508
|
+
child = spawn(command, [...args], {
|
|
509
|
+
cwd: options.cwd,
|
|
510
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
511
|
+
windowsHide: true,
|
|
512
|
+
});
|
|
513
|
+
} catch (error) {
|
|
514
|
+
reject(error);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
let stdout = "";
|
|
519
|
+
let stderr = "";
|
|
520
|
+
let settled = false;
|
|
521
|
+
|
|
522
|
+
const finish = (result: CommandResult | Error) => {
|
|
523
|
+
if (settled) return;
|
|
524
|
+
settled = true;
|
|
525
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
526
|
+
if (result instanceof Error) reject(result);
|
|
527
|
+
else resolve(result);
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const onAbort = () => {
|
|
531
|
+
try {
|
|
532
|
+
child.kill("SIGTERM");
|
|
533
|
+
} catch {
|
|
534
|
+
// Ignore: process may already be gone.
|
|
535
|
+
}
|
|
536
|
+
finish(new Error("aborted"));
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
child.stdout?.on("data", (chunk: Buffer | string) => {
|
|
540
|
+
stdout += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
541
|
+
});
|
|
542
|
+
child.stderr?.on("data", (chunk: Buffer | string) => {
|
|
543
|
+
stderr += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
544
|
+
});
|
|
545
|
+
child.on("error", finish);
|
|
546
|
+
child.on("close", (code) => finish({ stdout, stderr, exitCode: code }));
|
|
547
|
+
|
|
548
|
+
if (options.signal.aborted) {
|
|
549
|
+
onAbort();
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function defaultClock(): Clock {
|
|
557
|
+
return {
|
|
558
|
+
setInterval(callback, ms) {
|
|
559
|
+
const handle = setInterval(callback, ms);
|
|
560
|
+
(handle as { unref?: () => void }).unref?.();
|
|
561
|
+
return handle;
|
|
562
|
+
},
|
|
563
|
+
clearInterval(handle) {
|
|
564
|
+
clearInterval(handle as ReturnType<typeof setInterval>);
|
|
565
|
+
},
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
class GitFooterCache {
|
|
570
|
+
private readonly cwd: () => string;
|
|
571
|
+
private readonly canRun: () => boolean;
|
|
572
|
+
private readonly runner: CommandRunner;
|
|
573
|
+
private readonly clock: Clock;
|
|
574
|
+
private readonly refreshIntervalMs: number;
|
|
575
|
+
private readonly gitTimeoutMs: number;
|
|
576
|
+
private readonly ghTimeoutMs: number;
|
|
577
|
+
private readonly onChange: (() => void) | undefined;
|
|
578
|
+
|
|
579
|
+
private intervalHandle: TimerHandle | undefined;
|
|
580
|
+
private readonly inflightControllers = new Set<AbortController>();
|
|
581
|
+
private disposed = false;
|
|
582
|
+
private refreshInFlight: Promise<void> | undefined;
|
|
583
|
+
private statusSnapshot: GitStatusSnapshot | undefined;
|
|
584
|
+
private pullRequestSnapshot: PullRequestSnapshot | undefined;
|
|
585
|
+
private lastSeenBranch: string | undefined;
|
|
586
|
+
|
|
587
|
+
constructor(options: GitFooterCacheOptions) {
|
|
588
|
+
this.cwd = options.cwd;
|
|
589
|
+
this.canRun = options.canRun ?? (() => true);
|
|
590
|
+
this.runner = options.runner ?? defaultRunner;
|
|
591
|
+
this.clock = options.clock ?? defaultClock();
|
|
592
|
+
this.refreshIntervalMs = options.refreshIntervalMs ?? DEFAULT_CONFIG.gitStatus.refreshIntervalMs;
|
|
593
|
+
this.gitTimeoutMs = options.gitTimeoutMs ?? DEFAULT_CONFIG.gitStatus.gitTimeoutMs;
|
|
594
|
+
this.ghTimeoutMs = options.ghTimeoutMs ?? DEFAULT_CONFIG.gitStatus.ghTimeoutMs;
|
|
595
|
+
this.onChange = options.onChange;
|
|
596
|
+
|
|
597
|
+
this.intervalHandle = this.clock.setInterval(() => {
|
|
598
|
+
void this.refresh();
|
|
599
|
+
}, this.refreshIntervalMs);
|
|
600
|
+
void this.refresh();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
getStatusSnapshot(): GitStatusSnapshot | undefined {
|
|
604
|
+
return this.statusSnapshot;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
getPullRequestSnapshot(): PullRequestSnapshot | undefined {
|
|
608
|
+
return this.pullRequestSnapshot;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
refresh(): Promise<void> {
|
|
612
|
+
if (this.disposed) return Promise.resolve();
|
|
613
|
+
if (!this.canRun()) {
|
|
614
|
+
const previousStatusSnapshot = this.statusSnapshot;
|
|
615
|
+
const previousPullRequestSnapshot = this.pullRequestSnapshot;
|
|
616
|
+
this.statusSnapshot = undefined;
|
|
617
|
+
this.pullRequestSnapshot = undefined;
|
|
618
|
+
this.lastSeenBranch = undefined;
|
|
619
|
+
this.emitChangeIfSnapshotsChanged(previousStatusSnapshot, previousPullRequestSnapshot);
|
|
620
|
+
return Promise.resolve();
|
|
621
|
+
}
|
|
622
|
+
if (this.refreshInFlight) return this.refreshInFlight;
|
|
623
|
+
const run = this.runRefresh()
|
|
624
|
+
.finally(() => {
|
|
625
|
+
this.refreshInFlight = undefined;
|
|
626
|
+
})
|
|
627
|
+
.catch(() => undefined);
|
|
628
|
+
this.refreshInFlight = run;
|
|
629
|
+
return run;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private async runRefresh(): Promise<void> {
|
|
633
|
+
const previousStatusSnapshot = this.statusSnapshot;
|
|
634
|
+
const previousPullRequestSnapshot = this.pullRequestSnapshot;
|
|
635
|
+
|
|
636
|
+
const result = await this.fetchGitStatus();
|
|
637
|
+
if (this.disposed) return;
|
|
638
|
+
if (result.kind === "transient") return;
|
|
639
|
+
if (result.kind === "not-a-repo") {
|
|
640
|
+
this.statusSnapshot = undefined;
|
|
641
|
+
this.pullRequestSnapshot = undefined;
|
|
642
|
+
this.lastSeenBranch = undefined;
|
|
643
|
+
this.emitChangeIfSnapshotsChanged(previousStatusSnapshot, previousPullRequestSnapshot);
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const status = result.status;
|
|
648
|
+
this.statusSnapshot = status;
|
|
649
|
+
|
|
650
|
+
const branch = typeof status.branch === "string" ? status.branch : undefined;
|
|
651
|
+
const isValidBranch = !!branch && branch !== "detached";
|
|
652
|
+
const branchChanged = branch !== this.lastSeenBranch;
|
|
653
|
+
|
|
654
|
+
if (branchChanged) this.pullRequestSnapshot = undefined;
|
|
655
|
+
this.lastSeenBranch = branch;
|
|
656
|
+
|
|
657
|
+
if (!isValidBranch) {
|
|
658
|
+
this.pullRequestSnapshot = undefined;
|
|
659
|
+
this.emitChangeIfSnapshotsChanged(previousStatusSnapshot, previousPullRequestSnapshot);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const pr = await this.fetchPullRequest();
|
|
664
|
+
if (this.disposed) return;
|
|
665
|
+
if (pr !== undefined) this.pullRequestSnapshot = pr;
|
|
666
|
+
else if (branchChanged) this.pullRequestSnapshot = undefined;
|
|
667
|
+
|
|
668
|
+
this.emitChangeIfSnapshotsChanged(previousStatusSnapshot, previousPullRequestSnapshot);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
private emitChangeIfSnapshotsChanged(
|
|
672
|
+
previousStatusSnapshot: GitStatusSnapshot | undefined,
|
|
673
|
+
previousPullRequestSnapshot: PullRequestSnapshot | undefined,
|
|
674
|
+
): void {
|
|
675
|
+
if (this.disposed) return;
|
|
676
|
+
if (
|
|
677
|
+
gitStatusSnapshotsEqual(previousStatusSnapshot, this.statusSnapshot)
|
|
678
|
+
&& pullRequestSnapshotsEqual(previousPullRequestSnapshot, this.pullRequestSnapshot)
|
|
679
|
+
) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
try {
|
|
683
|
+
this.onChange?.();
|
|
684
|
+
} catch {
|
|
685
|
+
// Rendering hooks should not break refreshes.
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
private async fetchGitStatus(): Promise<
|
|
690
|
+
| { kind: "ok"; status: GitStatusSnapshot }
|
|
691
|
+
| { kind: "not-a-repo" }
|
|
692
|
+
| { kind: "transient" }
|
|
693
|
+
> {
|
|
694
|
+
const result = await this.runCommandSafely("git", GIT_STATUS_ARGS, this.gitTimeoutMs);
|
|
695
|
+
if (!result) return { kind: "transient" };
|
|
696
|
+
if (result.exitCode !== 0) return { kind: "not-a-repo" };
|
|
697
|
+
return { kind: "ok", status: parseGitStatusPorcelainV2(result.stdout) };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
private async fetchPullRequest(): Promise<PullRequestSnapshot | undefined> {
|
|
701
|
+
const result = await this.runCommandSafely("gh", GH_PR_VIEW_ARGS, this.ghTimeoutMs);
|
|
702
|
+
if (!result || result.exitCode !== 0) return undefined;
|
|
703
|
+
return parsePullRequestJson(result.stdout);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private async runCommandSafely(
|
|
707
|
+
command: string,
|
|
708
|
+
args: readonly string[],
|
|
709
|
+
timeoutMs: number,
|
|
710
|
+
): Promise<CommandResult | undefined> {
|
|
711
|
+
if (this.disposed) return undefined;
|
|
712
|
+
const controller = new AbortController();
|
|
713
|
+
this.inflightControllers.add(controller);
|
|
714
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
715
|
+
try {
|
|
716
|
+
return await this.runner(command, args, { cwd: this.cwd(), signal: controller.signal });
|
|
717
|
+
} catch {
|
|
718
|
+
return undefined;
|
|
719
|
+
} finally {
|
|
720
|
+
clearTimeout(timeoutId);
|
|
721
|
+
this.inflightControllers.delete(controller);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
dispose(): void {
|
|
726
|
+
if (this.disposed) return;
|
|
727
|
+
this.disposed = true;
|
|
728
|
+
if (this.intervalHandle !== undefined) {
|
|
729
|
+
this.clock.clearInterval(this.intervalHandle);
|
|
730
|
+
this.intervalHandle = undefined;
|
|
731
|
+
}
|
|
732
|
+
for (const controller of this.inflightControllers) controller.abort();
|
|
733
|
+
this.inflightControllers.clear();
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
242
737
|
function clearUsageState(state: UsageSessionState): void {
|
|
243
738
|
state.snapshot = undefined;
|
|
244
739
|
state.lastFetchedAt = undefined;
|
|
@@ -302,6 +797,28 @@ async function refreshUsageIfNeeded(
|
|
|
302
797
|
export default function (pi: ExtensionAPI) {
|
|
303
798
|
const states = new WeakMap<object, UsageSessionState>();
|
|
304
799
|
|
|
800
|
+
function disposeGitCache(state: UsageSessionState): void {
|
|
801
|
+
state.gitCache?.dispose();
|
|
802
|
+
state.gitCache = undefined;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function ensureGitCache(ctx: ExtensionContext, state: UsageSessionState): void {
|
|
806
|
+
if (!shouldShowGitStatus(state.config) || !canRunProjectGit(ctx)) {
|
|
807
|
+
disposeGitCache(state);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (state.gitCache) return;
|
|
812
|
+
state.gitCache = new GitFooterCache({
|
|
813
|
+
cwd: () => ctx.cwd,
|
|
814
|
+
canRun: () => canRunProjectGit(ctx),
|
|
815
|
+
refreshIntervalMs: state.config.gitStatus.refreshIntervalMs,
|
|
816
|
+
gitTimeoutMs: state.config.gitStatus.gitTimeoutMs,
|
|
817
|
+
ghTimeoutMs: state.config.gitStatus.ghTimeoutMs,
|
|
818
|
+
onChange: () => state.requestRender?.(),
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
|
|
305
822
|
pi.on("session_start", (_event, ctx) => {
|
|
306
823
|
const state: UsageSessionState = {
|
|
307
824
|
authStorage: AuthStorage.create(),
|
|
@@ -310,6 +827,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
310
827
|
};
|
|
311
828
|
states.set(ctx.sessionManager, state);
|
|
312
829
|
|
|
830
|
+
ensureGitCache(ctx, state);
|
|
831
|
+
|
|
313
832
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
314
833
|
const unsub = footerData.onBranchChange(() => tui.requestRender());
|
|
315
834
|
state.requestRender = () => tui.requestRender();
|
|
@@ -318,12 +837,22 @@ export default function (pi: ExtensionAPI) {
|
|
|
318
837
|
return {
|
|
319
838
|
dispose() {
|
|
320
839
|
if (state.requestRender) state.requestRender = undefined;
|
|
840
|
+
disposeGitCache(state);
|
|
321
841
|
unsub();
|
|
322
842
|
},
|
|
323
843
|
invalidate() {},
|
|
324
844
|
render(width: number): string[] {
|
|
325
845
|
const repo = basename(ctx.cwd);
|
|
326
|
-
const
|
|
846
|
+
const gitStatus = shouldShowGitStatus(state.config)
|
|
847
|
+
? formatGitFooterStatus(
|
|
848
|
+
state.gitCache?.getStatusSnapshot(),
|
|
849
|
+
state.gitCache?.getPullRequestSnapshot(),
|
|
850
|
+
)
|
|
851
|
+
: undefined;
|
|
852
|
+
const branch = footerData.getGitBranch()
|
|
853
|
+
?? state.gitCache?.getStatusSnapshot()?.branch
|
|
854
|
+
?? "";
|
|
855
|
+
const branchText = gitStatus ? [branch, gitStatus].filter(Boolean).join(" · ") : branch;
|
|
327
856
|
|
|
328
857
|
const usage = ctx.getContextUsage();
|
|
329
858
|
const context = usage?.percent == null ? "?" : `${usage.percent.toFixed(1)}%`;
|
|
@@ -337,7 +866,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
337
866
|
const thinking = pi.getThinkingLevel();
|
|
338
867
|
const modelText = thinking === "off" ? model : `${model} ${thinking}`;
|
|
339
868
|
|
|
340
|
-
const branchStyled = theme.fg("dim",
|
|
869
|
+
const branchStyled = theme.fg("dim", branchText);
|
|
341
870
|
const repoStyled = theme.fg("dim", repo);
|
|
342
871
|
const contextParts: string[] = [];
|
|
343
872
|
if (state.config.context.showPercent) contextParts.push(theme.fg("dim", context));
|
|
@@ -386,5 +915,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
386
915
|
const state = states.get(ctx.sessionManager);
|
|
387
916
|
if (!state) return;
|
|
388
917
|
void refreshUsageIfNeeded(ctx, state);
|
|
918
|
+
ensureGitCache(ctx, state);
|
|
919
|
+
void state.gitCache?.refresh();
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
pi.on("session_shutdown", (_event, ctx) => {
|
|
923
|
+
const state = states.get(ctx.sessionManager);
|
|
924
|
+
if (!state) return;
|
|
925
|
+
disposeGitCache(state);
|
|
389
926
|
});
|
|
390
927
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-openai-fast",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.79.10
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
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",
|