@diegopetrucci/pi-extensions 0.1.73 → 0.1.76

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.
Files changed (93) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +9 -4
  3. package/extensions/agent-workflow-audit/index.ts +1398 -1009
  4. package/extensions/agent-workflow-audit/package.json +1 -1
  5. package/extensions/annotate-git-diff/README.md +3 -1
  6. package/extensions/annotate-git-diff/clipboard.ts +109 -100
  7. package/extensions/annotate-git-diff/git.ts +853 -767
  8. package/extensions/annotate-git-diff/glimpseui.d.ts +87 -78
  9. package/extensions/annotate-git-diff/index.ts +712 -671
  10. package/extensions/annotate-git-diff/package.json +1 -1
  11. package/extensions/annotate-git-diff/prompt.ts +45 -45
  12. package/extensions/annotate-git-diff/quiet-glimpse.ts +161 -155
  13. package/extensions/annotate-git-diff/review-server.ts +337 -311
  14. package/extensions/annotate-git-diff/types.ts +132 -132
  15. package/extensions/annotate-git-diff/ui.ts +27 -27
  16. package/extensions/annotate-git-diff/watch.ts +74 -74
  17. package/extensions/annotate-git-diff/web/app.js +1947 -1884
  18. package/extensions/annotate-git-diff/web/index.html +5 -6
  19. package/extensions/annotate-last-message/README.md +1 -1
  20. package/extensions/annotate-last-message/glimpseui.d.ts +87 -78
  21. package/extensions/annotate-last-message/index.ts +210 -206
  22. package/extensions/annotate-last-message/package.json +1 -1
  23. package/extensions/annotate-last-message/prompt.ts +87 -66
  24. package/extensions/annotate-last-message/quiet-glimpse.ts +161 -155
  25. package/extensions/annotate-last-message/session.ts +94 -89
  26. package/extensions/annotate-last-message/theme.ts +199 -193
  27. package/extensions/annotate-last-message/types.ts +23 -23
  28. package/extensions/annotate-last-message/ui.ts +27 -25
  29. package/extensions/annotate-last-message/web/app.js +4 -7
  30. package/extensions/annotate-last-message/web/md-renderer.js +1 -1
  31. package/extensions/brrr/README.md +1 -1
  32. package/extensions/brrr/index.ts +213 -191
  33. package/extensions/brrr/package.json +1 -1
  34. package/extensions/claude-fast/README.md +5 -79
  35. package/extensions/claude-fast/index.ts +31 -289
  36. package/extensions/claude-fast/package.json +2 -3
  37. package/extensions/code-reviewer/README.md +1 -1
  38. package/extensions/code-reviewer/index.ts +1598 -1350
  39. package/extensions/code-reviewer/package.json +1 -1
  40. package/extensions/confirm-destructive/README.md +1 -1
  41. package/extensions/confirm-destructive/index.ts +55 -51
  42. package/extensions/confirm-destructive/package.json +1 -1
  43. package/extensions/context-cap/index.ts +312 -304
  44. package/extensions/context-cap/package.json +1 -1
  45. package/extensions/context-inspector/index.ts +1211 -1064
  46. package/extensions/context-inspector/package.json +1 -1
  47. package/extensions/contrarian/index.ts +1854 -1735
  48. package/extensions/contrarian/package.json +1 -1
  49. package/extensions/dirty-repo-guard/index.ts +41 -41
  50. package/extensions/dynamic-context-pruning/README.md +6 -38
  51. package/extensions/dynamic-context-pruning/index.ts +2124 -1980
  52. package/extensions/dynamic-context-pruning/package.json +1 -1
  53. package/extensions/fast/README.md +71 -21
  54. package/extensions/fast/index.ts +513 -313
  55. package/extensions/fast/package.json +1 -1
  56. package/extensions/git-footer/README.md +1 -1
  57. package/extensions/git-footer/index.ts +458 -411
  58. package/extensions/git-footer/package.json +2 -3
  59. package/extensions/gnosis/index.ts +180 -130
  60. package/extensions/gnosis/package.json +1 -1
  61. package/extensions/inline-bash/index.ts +88 -88
  62. package/extensions/inline-bash/package.json +1 -1
  63. package/extensions/librarian/index.ts +1669 -1419
  64. package/extensions/librarian/package.json +1 -1
  65. package/extensions/minimal-footer/index.ts +994 -1012
  66. package/extensions/minimal-footer/openai-usage.ts +195 -190
  67. package/extensions/notify/index.ts +222 -230
  68. package/extensions/notify/package.json +1 -1
  69. package/extensions/openai-fast/README.md +5 -73
  70. package/extensions/openai-fast/index.ts +32 -272
  71. package/extensions/openai-fast/package.json +2 -3
  72. package/extensions/oracle/index.ts +1731 -1625
  73. package/extensions/oracle/package.json +1 -1
  74. package/extensions/permission-gate/index.ts +658 -608
  75. package/extensions/permission-gate/package.json +1 -1
  76. package/extensions/permission-gate/powershell-safety.ts +840 -793
  77. package/extensions/project-mcp-json/.pi-fleet-tested-version +1 -0
  78. package/extensions/project-mcp-json/README.md +199 -0
  79. package/extensions/project-mcp-json/config.ts +564 -0
  80. package/extensions/project-mcp-json/index.ts +119 -0
  81. package/extensions/project-mcp-json/package.json +38 -0
  82. package/extensions/quiet-tools/index.ts +282 -264
  83. package/extensions/review/README.md +2 -2
  84. package/extensions/review/index.ts +1745 -1872
  85. package/extensions/review/package.json +1 -1
  86. package/extensions/todo/index.ts +284 -265
  87. package/extensions/triage-comments/README.md +2 -2
  88. package/extensions/triage-comments/index.ts +2294 -1882
  89. package/extensions/triage-comments/package.json +1 -1
  90. package/package.json +6 -3
  91. package/assets/social-preview.png +0 -0
  92. package/extensions/claude-fast/claude-fast.example.json +0 -4
  93. package/extensions/openai-fast/openai-fast.example.json +0 -4
@@ -1,293 +1,35 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import {
4
- CONFIG_DIR_NAME,
5
- getAgentDir,
6
- type ExtensionAPI,
7
- type ExtensionContext,
8
- } from "@earendil-works/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
9
2
 
10
3
  const EXTENSION_ID = "claude-fast";
11
- const PROVIDER_ID = "anthropic";
12
- const API_ID = "anthropic-messages";
13
- const FAST_SPEED = "fast";
14
4
  const FAST_BETA = "fast-mode-2026-02-01";
15
- const CLAUDE_CODE_OAUTH_BETAS = ["claude-code-20250219", "oauth-2025-04-20"];
16
- const SUPPORTED_MODELS = new Set(["claude-opus-4-8", "claude-opus-5"]);
17
-
18
- const DEFAULT_CONFIG: ClaudeFastConfig = {
19
- enabled: false,
20
- showStatus: true,
21
- };
22
-
23
- type FastOverride = "auto" | "on" | "off";
24
-
25
- type ClaudeFastConfig = {
26
- /** Default Fast-mode state when there is no session override. */
27
- enabled: boolean;
28
- /** Show a compact `fast` status when Fast mode is active for the current model. */
29
- showStatus: boolean;
30
- };
31
-
32
- type SessionState = {
33
- config: ClaudeFastConfig;
34
- override: FastOverride;
35
- lastInjectedAt?: number;
36
- lastInjectedModel?: string;
37
- };
38
-
39
- type ProjectConfigContext = {
40
- cwd: string;
41
- isProjectTrusted?: () => boolean;
42
- };
43
-
44
- type RecursivePartial<T> = {
45
- [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P];
46
- };
47
-
48
- type PayloadRecord = Record<string, unknown>;
49
-
50
- type Eligibility = {
51
- eligible: boolean;
52
- modelKey: string;
53
- reason?: string;
54
- };
55
-
56
- type HeaderModel = {
57
- headers?: Record<string, string>;
58
- };
59
-
60
- function readConfigFile(path: string): RecursivePartial<ClaudeFastConfig> {
61
- if (!existsSync(path)) return {};
62
-
63
- try {
64
- const parsed = JSON.parse(readFileSync(path, "utf-8"));
65
- return isPayloadRecord(parsed) ? (parsed as RecursivePartial<ClaudeFastConfig>) : {};
66
- } catch (error) {
67
- console.error(`Warning: Could not parse ${path}: ${error}`);
68
- return {};
69
- }
70
- }
71
-
72
- function normalizeBoolean(value: unknown, fallback: boolean): boolean {
73
- return typeof value === "boolean" ? value : fallback;
74
- }
75
-
76
- function mergeConfig(
77
- base: ClaudeFastConfig,
78
- overrides: RecursivePartial<ClaudeFastConfig>,
79
- ): ClaudeFastConfig {
80
- return {
81
- enabled: normalizeBoolean(overrides.enabled, base.enabled),
82
- showStatus: normalizeBoolean(overrides.showStatus, base.showStatus),
83
- };
84
- }
85
-
86
- function canReadProjectConfig(ctx: ProjectConfigContext): boolean {
87
- return typeof ctx.isProjectTrusted === "function" && ctx.isProjectTrusted();
88
- }
89
-
90
- function findProjectConfigPath(cwd: string): string {
91
- let current = cwd;
92
- while (true) {
93
- const candidate = join(current, CONFIG_DIR_NAME, "claude-fast.json");
94
- if (existsSync(candidate)) return candidate;
95
-
96
- const parent = dirname(current);
97
- if (parent === current) return join(cwd, CONFIG_DIR_NAME, "claude-fast.json");
98
- current = parent;
99
- }
100
- }
101
-
102
- function loadConfig(ctx: ProjectConfigContext): ClaudeFastConfig {
103
- const globalConfig = readConfigFile(join(getAgentDir(), "extensions", "claude-fast.json"));
104
- const projectConfig = canReadProjectConfig(ctx) ? readConfigFile(findProjectConfigPath(ctx.cwd)) : {};
105
- return mergeConfig(mergeConfig(DEFAULT_CONFIG, globalConfig), projectConfig);
106
- }
107
-
108
- function isPayloadRecord(payload: unknown): payload is PayloadRecord {
109
- return typeof payload === "object" && payload !== null && !Array.isArray(payload);
110
- }
111
-
112
- function modelKey(ctx: ExtensionContext): string {
113
- const model = ctx.model;
114
- return model ? `${model.provider}/${model.id}` : "no-model";
115
- }
116
-
117
- function isFastEnabled(state: SessionState): boolean {
118
- if (state.override === "on") return true;
119
- if (state.override === "off") return false;
120
- return state.config.enabled;
121
- }
122
-
123
- function describeMode(state: SessionState): string {
124
- if (state.override === "on") return "on (session override)";
125
- if (state.override === "off") return "off (session override)";
126
- return state.config.enabled ? "on (config default)" : "off (config default)";
127
- }
128
-
129
- function getEligibility(ctx: ExtensionContext): Eligibility {
130
- const model = ctx.model;
131
- if (!model) {
132
- return { eligible: false, modelKey: "no-model", reason: "no model is selected" };
133
- }
134
-
135
- const key = `${model.provider}/${model.id}`;
136
- if (model.provider !== PROVIDER_ID) {
137
- return {
138
- eligible: false,
139
- modelKey: key,
140
- reason: `current provider is ${model.provider}, not ${PROVIDER_ID}`,
141
- };
142
- }
143
-
144
- if (model.api !== API_ID) {
145
- return {
146
- eligible: false,
147
- modelKey: key,
148
- reason: `current API is ${model.api}, not ${API_ID}`,
149
- };
150
- }
151
-
152
- if (!SUPPORTED_MODELS.has(model.id)) {
153
- return {
154
- eligible: false,
155
- modelKey: key,
156
- reason: "Fast mode is only enabled for Claude Opus 4.8 and Claude Opus 5",
157
- };
158
- }
159
-
160
- return { eligible: true, modelKey: key };
161
- }
162
-
163
- function splitBetaHeader(value: string | undefined): string[] {
164
- return (value ?? "")
165
- .split(",")
166
- .map((part) => part.trim())
167
- .filter(Boolean);
168
- }
169
-
170
- function syncModelBetaHeader(ctx: ExtensionContext, state: SessionState): void {
171
- const model = ctx.model as (typeof ctx.model & HeaderModel) | undefined;
172
- if (!model || model.provider !== PROVIDER_ID || model.api !== API_ID) return;
173
-
174
- const shouldEnable = isFastEnabled(state) && getEligibility(ctx).eligible;
175
- const headers = { ...(model.headers ?? {}) };
176
- const existing = splitBetaHeader(headers["anthropic-beta"] ?? headers["Anthropic-Beta"]);
177
- const requiredBase = ctx.modelRegistry.isUsingOAuth(model) ? CLAUDE_CODE_OAUTH_BETAS : [];
178
- const next = shouldEnable
179
- ? Array.from(new Set([...existing, ...requiredBase, FAST_BETA]))
180
- : existing.filter((beta) => beta !== FAST_BETA);
181
-
182
- delete headers["Anthropic-Beta"];
183
- if (next.length > 0) headers["anthropic-beta"] = next.join(",");
184
- else delete headers["anthropic-beta"];
185
- model.headers = headers;
186
- }
187
-
188
- function updateStatus(ctx: ExtensionContext, state: SessionState): void {
189
- syncModelBetaHeader(ctx, state);
190
- if (!ctx.hasUI) return;
191
- if (!state.config.showStatus) {
192
- ctx.ui.setStatus(EXTENSION_ID, undefined);
193
- return;
194
- }
195
-
196
- const eligibility = getEligibility(ctx);
197
- ctx.ui.setStatus(
198
- EXTENSION_ID,
199
- isFastEnabled(state) && eligibility.eligible ? "fast" : undefined,
200
- );
201
- }
202
-
203
- function getStatusMessage(ctx: ExtensionContext, state: SessionState): string {
204
- const enabled = isFastEnabled(state);
205
- const eligibility = getEligibility(ctx);
206
- const active = enabled && eligibility.eligible;
207
- const injected = state.lastInjectedAt
208
- ? ` Last injected for ${state.lastInjectedModel ?? "unknown model"} ${Math.max(0, Math.round((Date.now() - state.lastInjectedAt) / 1000))}s ago.`
209
- : "";
210
-
211
- if (active) {
212
- return `Claude Fast mode is ${describeMode(state)} and active for ${eligibility.modelKey}; requests will use speed=${FAST_SPEED}.${injected}`;
213
- }
214
-
215
- if (enabled) {
216
- return `Claude Fast mode is ${describeMode(state)}, but inactive for ${eligibility.modelKey}: ${eligibility.reason}.${injected}`;
217
- }
218
-
219
- return `Claude Fast mode is ${describeMode(state)}. Current model: ${eligibility.modelKey}.${injected}`;
220
- }
221
-
222
- function injectFastSpeed(
223
- payload: unknown,
224
- ctx: ExtensionContext,
225
- state: SessionState,
226
- ): PayloadRecord | undefined {
227
- if (!isFastEnabled(state)) return undefined;
228
- if (!getEligibility(ctx).eligible) return undefined;
229
- if (!isPayloadRecord(payload)) return undefined;
230
- if (payload.model !== ctx.model?.id) return undefined;
231
- if ("speed" in payload) return undefined;
232
-
233
- state.lastInjectedAt = Date.now();
234
- state.lastInjectedModel = modelKey(ctx);
235
- return {
236
- ...payload,
237
- speed: FAST_SPEED,
238
- };
239
- }
240
-
241
- export default function claudeFastExtension(pi: ExtensionAPI) {
242
- const states = new WeakMap<object, SessionState>();
243
-
244
- function getState(ctx: ExtensionContext): SessionState {
245
- let state = states.get(ctx.sessionManager);
246
- if (!state) {
247
- state = {
248
- config: loadConfig(ctx),
249
- override: "auto",
250
- };
251
- states.set(ctx.sessionManager, state);
252
- }
253
- return state;
254
- }
255
-
256
- pi.on("session_start", (_event, ctx) => {
257
- const state: SessionState = {
258
- config: loadConfig(ctx),
259
- override: "auto",
260
- };
261
- states.set(ctx.sessionManager, state);
262
- updateStatus(ctx, state);
263
- });
264
-
265
- pi.on("model_select", (_event, ctx) => {
266
- updateStatus(ctx, getState(ctx));
267
- });
268
-
269
- pi.on("before_provider_request", (event, ctx) => {
270
- const state = getState(ctx);
271
- const nextPayload = injectFastSpeed(event.payload, ctx, state);
272
- updateStatus(ctx, state);
273
- return nextPayload;
274
- });
275
-
276
- pi.registerCommand("claude-fast", {
277
- description: "Toggle Claude Fast mode for supported Anthropic Claude Opus models",
278
- getArgumentCompletions: () => null,
279
- handler: async (args, ctx) => {
280
- const state = getState(ctx);
281
- const action = args.trim();
282
-
283
- if (!action) {
284
- state.override = isFastEnabled(state) ? "off" : "on";
285
- updateStatus(ctx, state);
286
- ctx.ui.notify(getStatusMessage(ctx, state), "info");
287
- return;
288
- }
289
-
290
- ctx.ui.notify("Usage: /claude-fast", "warning");
291
- },
292
- });
5
+ const DEPRECATION_NOTICE =
6
+ "claude-fast is deprecated and no longer changes provider requests. Use the unified fast extension (/fast) instead.";
7
+
8
+ /**
9
+ * Deprecated leftover of the provider-specific Fast stack.
10
+ * The collection loads extensions/fast only. This package keeps the published
11
+ * npm name working. It does not read config, register commands, or inject Fast
12
+ * fields. Startup only clears leftover footer status and the Fast beta header,
13
+ * which /reload otherwise keeps from the previous extension runtime.
14
+ */
15
+ function stripFastBeta(headers: Record<string, string> | undefined): void {
16
+ if (!headers) return;
17
+ for (const key of Object.keys(headers)) {
18
+ if (key.toLowerCase() !== "anthropic-beta") continue;
19
+ const next = headers[key]
20
+ .split(",")
21
+ .map((part) => part.trim())
22
+ .filter((part) => part && part !== FAST_BETA);
23
+ if (next.length === 0) delete headers[key];
24
+ else headers[key] = next.join(",");
25
+ }
26
+ }
27
+
28
+ export default function claudeFastExtension(pi: ExtensionAPI): void {
29
+ pi.on("session_start", (_event, ctx) => {
30
+ stripFastBeta(ctx.model?.headers);
31
+ if (!ctx.hasUI) return;
32
+ ctx.ui.setStatus(EXTENSION_ID, undefined);
33
+ ctx.ui.notify(DEPRECATION_NOTICE, "warning");
34
+ });
293
35
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-claude-fast",
3
- "version": "0.1.15",
4
- "description": "A pi extension that enables Anthropic Claude Fast mode for supported Claude Opus models by injecting speed=fast.",
3
+ "version": "0.1.16",
4
+ "description": "Deprecated standalone leftover. Does not change provider requests; use @diegopetrucci/pi-fast.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi",
@@ -17,7 +17,6 @@
17
17
  },
18
18
  "files": [
19
19
  "index.ts",
20
- "claude-fast.example.json",
21
20
  "README.md",
22
21
  ".pi-fleet-tested-version"
23
22
  ],
@@ -59,7 +59,7 @@ The review prompt prioritizes:
59
59
  5. simplicity and maintainability
60
60
  6. tests and validation gaps
61
61
 
62
- The final output is concise and includes a verdict, findings, validation notes, a scope check, and run details that show the final selected model and effective thinking level.
62
+ The model report is concise and includes a verdict, findings, validation notes, and a scope check. The tool appends a run-details suffix with the final selected model, effective thinking level, turn count, tool calls, duration, working directory, and tools used.
63
63
 
64
64
  ## Read-only guarantees
65
65