@diegopetrucci/pi-extensions 0.1.65 → 0.1.67

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 (70) hide show
  1. package/.pi-fleet-tested-version +1 -1
  2. package/README.md +1 -1
  3. package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
  4. package/extensions/agent-workflow-audit/package.json +1 -1
  5. package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
  6. package/extensions/annotate-git-diff/README.md +3 -0
  7. package/extensions/annotate-git-diff/index.ts +49 -5
  8. package/extensions/annotate-git-diff/package.json +2 -1
  9. package/extensions/annotate-git-diff/quiet-glimpse.ts +24 -3
  10. package/extensions/annotate-git-diff/review-server.ts +379 -0
  11. package/extensions/annotate-git-diff/ui.ts +17 -41
  12. package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
  13. package/extensions/annotate-last-message/index.ts +28 -2
  14. package/extensions/annotate-last-message/package.json +1 -1
  15. package/extensions/annotate-last-message/quiet-glimpse.ts +24 -3
  16. package/extensions/brrr/.pi-fleet-tested-version +1 -1
  17. package/extensions/brrr/package.json +1 -1
  18. package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
  19. package/extensions/claude-fast/package.json +1 -1
  20. package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
  21. package/extensions/code-reviewer/index.ts +3 -1
  22. package/extensions/code-reviewer/package.json +1 -1
  23. package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
  24. package/extensions/confirm-destructive/package.json +1 -1
  25. package/extensions/context-cap/.pi-fleet-tested-version +1 -1
  26. package/extensions/context-cap/package.json +1 -1
  27. package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
  28. package/extensions/context-inspector/package.json +1 -1
  29. package/extensions/contrarian/.pi-fleet-tested-version +1 -1
  30. package/extensions/contrarian/index.ts +50 -28
  31. package/extensions/contrarian/package.json +1 -1
  32. package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
  33. package/extensions/dirty-repo-guard/package.json +1 -1
  34. package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
  35. package/extensions/dynamic-context-pruning/package.json +1 -1
  36. package/extensions/fast/.pi-fleet-tested-version +1 -1
  37. package/extensions/fast/package.json +1 -1
  38. package/extensions/git-footer/.pi-fleet-tested-version +1 -1
  39. package/extensions/git-footer/package.json +1 -1
  40. package/extensions/gnosis/.pi-fleet-tested-version +1 -1
  41. package/extensions/gnosis/package.json +1 -1
  42. package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
  43. package/extensions/illustrations-to-explain-things/package.json +1 -1
  44. package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
  45. package/extensions/inline-bash/package.json +1 -1
  46. package/extensions/librarian/.pi-fleet-tested-version +1 -1
  47. package/extensions/librarian/package.json +1 -1
  48. package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
  49. package/extensions/minimal-footer/package.json +1 -1
  50. package/extensions/notify/.pi-fleet-tested-version +1 -1
  51. package/extensions/notify/package.json +1 -1
  52. package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
  53. package/extensions/openai-fast/package.json +1 -1
  54. package/extensions/oracle/.pi-fleet-tested-version +1 -1
  55. package/extensions/oracle/index.ts +49 -28
  56. package/extensions/oracle/package.json +1 -1
  57. package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
  58. package/extensions/permission-gate/README.md +11 -3
  59. package/extensions/permission-gate/index.ts +70 -7
  60. package/extensions/permission-gate/package.json +4 -2
  61. package/extensions/permission-gate/powershell-safety.ts +1006 -0
  62. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  63. package/extensions/quiet-tools/package.json +1 -1
  64. package/extensions/review/.pi-fleet-tested-version +1 -1
  65. package/extensions/review/package.json +1 -1
  66. package/extensions/todo/.pi-fleet-tested-version +1 -1
  67. package/extensions/todo/package.json +1 -1
  68. package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
  69. package/extensions/triage-comments/package.json +1 -1
  70. package/package.json +4 -4
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-fast",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "A pi extension that enables Fast mode for supported OpenAI Codex and Anthropic Claude models based on the currently selected model.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-git-footer",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A TLH-style git status add-on for pi's built-in footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-gnosis",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "A pi extension that exposes the gnosis repo-local knowledge base CLI as an agent tool.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-illustrations-to-explain-things",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
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.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-inline-bash",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "A pi extension that expands inline bash commands in user prompts.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-librarian",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "A pi GitHub research scout with a toggleable local repo checkout cache under the user's OS cache directory.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-minimal-footer",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "description": "A minimal custom footer for pi.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-notify",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "A pi extension that sends a notification when the agent is ready for input.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-openai-fast",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "A pi extension that enables OpenAI Codex Fast mode for ChatGPT-auth GPT-5.5 and GPT-5.6 Codex variants by injecting the priority service tier.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -115,6 +115,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
115
115
  "claude-opus-4-5",
116
116
  "claude-opus-4-1",
117
117
  "claude-opus-4",
118
+ "openai.gpt-5.6-sol",
119
+ "openai.gpt-5.6-terra",
120
+ "openai.gpt-5.6-luna",
118
121
  "claude-sonnet-5",
119
122
  "claude-sonnet-4-6",
120
123
  "claude-sonnet-4-5",
@@ -125,6 +128,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
125
128
  "minimax-m2.5",
126
129
  "minimax-m2.1",
127
130
  "zai.glm-5",
131
+ "xai.grok-4.6",
128
132
  ],
129
133
  anthropic: [
130
134
  "claude-opus-5",
@@ -160,6 +164,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
160
164
  baseten: [
161
165
  "moonshotai/Kimi-K3",
162
166
  "moonshotai/Kimi-K2.7-Code",
167
+ "zai-org/GLM-5.3",
163
168
  "deepseek-ai/DeepSeek-V4-Pro-0813",
164
169
  "deepseek-ai/DeepSeek-V4-Pro",
165
170
  "zai-org/GLM-5.2",
@@ -176,62 +181,67 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
176
181
  "zai-org/GLM-5.2-Fast",
177
182
  "zai-org/GLM-4.7",
178
183
  ],
179
- cerebras: ["gpt-oss-120b", "zai-glm-4.7", "gemma-4-31b"],
184
+ cerebras: ["gpt-oss-120b", "gemma-4-31b"],
180
185
  "cloudflare-ai-gateway": [
181
186
  "claude-fable-5",
182
- "claude-opus-4-7",
183
- "claude-opus-4-6",
184
- "claude-opus-4-5",
187
+ "claude-opus-5",
188
+ "claude-opus-4.8",
189
+ "claude-opus-4.7",
190
+ "claude-opus-4.6",
191
+ "claude-opus-4.5",
185
192
  "gpt-5.5",
186
193
  "gpt-5.4",
187
- "gpt-5.3-codex",
194
+ "workers-ai/@cf/moonshotai/kimi-k2.7-code",
195
+ "workers-ai/@cf/deepseek-ai/deepseek-v4-pro-0813",
196
+ "workers-ai/@cf/zai-org/glm-5.3",
197
+ "workers-ai/@cf/zai-org/glm-5.2",
188
198
  "workers-ai/@cf/moonshotai/kimi-k2.6",
189
199
  "workers-ai/@cf/nvidia/nemotron-3-120b-a12b",
200
+ "workers-ai/@cf/openai/gpt-oss-120b",
190
201
  "workers-ai/@cf/zai-org/glm-4.7-flash",
191
202
  ],
192
203
  "cloudflare-workers-ai": [
193
204
  "@cf/moonshotai/kimi-k2.7-code",
205
+ "@cf/deepseek-ai/deepseek-v4-pro-0813",
206
+ "@cf/zai-org/glm-5.3",
194
207
  "@cf/zai-org/glm-5.2",
195
208
  "@cf/moonshotai/kimi-k2.6",
196
209
  "@cf/nvidia/nemotron-3-120b-a12b",
197
210
  "@cf/openai/gpt-oss-120b",
211
+ "@cf/qwen/qwen3.8-27b",
198
212
  "@cf/qwen/qwen3-30b-a3b-fp8",
199
213
  "@cf/zai-org/glm-4.7-flash",
200
214
  ],
201
215
  deepseek: ["deepseek-v4-pro", "deepseek-v4-flash"],
202
216
  fireworks: [
217
+ "accounts/fireworks/models/kimi-k3",
218
+ "accounts/fireworks/routers/kimi-k3-fast",
203
219
  "accounts/fireworks/models/deepseek-v4-pro",
204
220
  "accounts/fireworks/models/kimi-k2p7-code",
205
- "accounts/fireworks/routers/kimi-k2p7-code-fast",
206
221
  "accounts/fireworks/models/glm-5p2",
207
222
  "accounts/fireworks/routers/glm-5p2-fast",
208
223
  "accounts/fireworks/models/minimax-m3",
209
224
  "accounts/fireworks/models/kimi-k2p6",
210
- "accounts/fireworks/routers/kimi-k2p6-turbo",
211
- "accounts/fireworks/routers/kimi-k2p6-fast",
212
- "accounts/fireworks/models/minimax-m2p7",
213
225
  "accounts/fireworks/models/qwen3p7-plus",
214
226
  "accounts/fireworks/models/gpt-oss-120b",
215
- "accounts/fireworks/models/gpt-oss-20b",
216
227
  ],
217
228
  "github-copilot": [
218
229
  "claude-opus-5",
219
230
  "claude-fable-5",
220
231
  "claude-opus-4.8",
221
232
  "claude-opus-4.7",
222
- "claude-opus-4.6",
223
- "claude-opus-4.5",
233
+ "gpt-6-astra",
234
+ "gpt-5.6-sol",
235
+ "gpt-5.6-terra",
236
+ "gpt-5.6-luna",
224
237
  "gpt-5.5",
225
238
  "gpt-5.4",
226
239
  "gpt-5.3-codex",
227
- "gpt-5.2-codex",
228
- "gpt-5.2",
229
- "gemini-3.1-pro-preview",
230
240
  "gemini-3.5-flash",
231
241
  "claude-sonnet-5",
232
242
  "claude-sonnet-4.6",
233
- "claude-sonnet-4.5",
234
243
  "gpt-5-mini",
244
+ "kimi-k3",
235
245
  "kimi-k2.7-code",
236
246
  ],
237
247
  google: [
@@ -260,7 +270,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
260
270
  "openai/gpt-oss-20b",
261
271
  ],
262
272
  huggingface: [
273
+ "moonshotai/Kimi-K3",
263
274
  "zai-org/GLM-5.1",
275
+ "zai-org/GLM-5.3-Flash",
276
+ "deepseek-ai/DeepSeek-V4-Pro-0813",
264
277
  "deepseek-ai/DeepSeek-V4-Pro",
265
278
  "moonshotai/Kimi-K2.6",
266
279
  "MiniMaxAI/MiniMax-M2.7",
@@ -285,8 +298,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
285
298
  "mistral-medium-2508",
286
299
  "mistral-small-2603",
287
300
  "devstral-2512",
301
+ "zai-glm-5-2",
288
302
  ],
289
303
  openai: [
304
+ "gpt-6-astra",
290
305
  "gpt-5.6-sol",
291
306
  "gpt-5.6-terra",
292
307
  "gpt-5.6-luna",
@@ -306,6 +321,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
306
321
  "gpt-5-mini",
307
322
  ],
308
323
  "openai-codex": [
324
+ "gpt-6-astra",
309
325
  "gpt-5.6-sol",
310
326
  "gpt-5.6-terra",
311
327
  "gpt-5.6-luna",
@@ -334,6 +350,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
334
350
  "glm-5.2",
335
351
  "glm-5.1",
336
352
  "glm-5",
353
+ "kimi-k3",
337
354
  "kimi-k2.7-code",
338
355
  "kimi-k2.6",
339
356
  "kimi-k2.5",
@@ -344,6 +361,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
344
361
  ],
345
362
  "opencode-go": [
346
363
  "deepseek-v4-pro",
364
+ "glm-5.3",
347
365
  "glm-5.2",
348
366
  "glm-5.1",
349
367
  "qwen3.7-max",
@@ -353,6 +371,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
353
371
  "mimo-v2.5",
354
372
  "minimax-m3",
355
373
  "minimax-m2.7",
374
+ "kimi-k3",
356
375
  "kimi-k2.7-code",
357
376
  "kimi-k2.6",
358
377
  ],
@@ -361,9 +380,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
361
380
  "anthropic/claude-fable-5",
362
381
  "~anthropic/claude-fable-latest",
363
382
  "anthropic/claude-opus-4.8",
364
- "anthropic/claude-opus-4.8-fast",
365
383
  "anthropic/claude-opus-4.7",
366
- "anthropic/claude-opus-4.7-fast",
367
384
  "anthropic/claude-opus-4.6",
368
385
  "anthropic/claude-opus-4.5",
369
386
  "anthropic/claude-opus-4.1",
@@ -381,6 +398,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
381
398
  "~google/gemini-pro-latest",
382
399
  "google/gemini-3.5-flash",
383
400
  "google/gemini-2.5-pro",
401
+ "moonshotai/kimi-k3",
384
402
  "moonshotai/kimi-k2.7-code",
385
403
  "~moonshotai/kimi-latest",
386
404
  "moonshotai/kimi-k2.6",
@@ -393,6 +411,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
393
411
  "minimax/minimax-m3",
394
412
  "minimax/minimax-m2.7",
395
413
  "minimax/minimax-m2.1",
414
+ "z-ai/glm-5.3",
396
415
  "z-ai/glm-5.2",
397
416
  "z-ai/glm-5.1",
398
417
  ],
@@ -427,6 +446,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
427
446
  ],
428
447
  together: [
429
448
  "deepseek-ai/DeepSeek-V4-Pro",
449
+ "moonshotai/Kimi-K3",
450
+ "zai-org/GLM-5.3-Flash",
430
451
  "zai-org/GLM-5.2",
431
452
  "moonshotai/Kimi-K2.7-Code",
432
453
  "moonshotai/Kimi-K2.6",
@@ -459,6 +480,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
459
480
  "openai/gpt-5.2-codex",
460
481
  "openai/gpt-5.1-codex",
461
482
  "openai/gpt-5-codex",
483
+ "moonshotai/kimi-k3",
462
484
  "moonshotai/kimi-k2.7-code",
463
485
  "moonshotai/kimi-k2.7-code-highspeed",
464
486
  "moonshotai/kimi-k2.6",
@@ -476,6 +498,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
476
498
  "xai/grok-4.5",
477
499
  "xai/grok-4.20-reasoning",
478
500
  "minimax/minimax-m3",
501
+ "zai/glm-5.3",
479
502
  "zai/glm-5.2",
480
503
  "zai/glm-5.1",
481
504
  ],
@@ -483,21 +506,19 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
483
506
  "grok-4.6",
484
507
  "grok-4.5",
485
508
  "grok-4.3",
486
- "grok-build-0.1",
487
509
  ],
488
- xiaomi: ["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash"],
489
- "xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro"],
490
- "xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro"],
491
- "xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro"],
510
+ xiaomi: ["mimo-v2.5-pro-ultraspeed", "mimo-v2.5-pro", "mimo-v2.5"],
511
+ "xiaomi-token-plan-ams": ["mimo-v2.5-pro", "mimo-v2.5"],
512
+ "xiaomi-token-plan-cn": ["mimo-v2.5-pro", "mimo-v2.5"],
513
+ "xiaomi-token-plan-sgp": ["mimo-v2.5-pro", "mimo-v2.5"],
492
514
  nvidia: [
493
515
  "nvidia/nemotron-3-ultra-550b-a55b",
494
516
  "nvidia/nemotron-3-super-120b-a12b",
517
+ "moonshotai/kimi-k3",
518
+ "deepseek-ai/deepseek-v4-pro-0813",
495
519
  "moonshotai/kimi-k2.6",
496
- "z-ai/glm-5.2",
497
520
  "minimaxai/minimax-m3",
498
- "openai/gpt-oss-120b",
499
521
  "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
500
- "nvidia/nemotron-3-nano-30b-a3b",
501
522
  ],
502
523
  zai: [
503
524
  "glm-5.3",
@@ -512,8 +533,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
512
533
  "glm-5",
513
534
  "glm-4.7",
514
535
  ],
515
- moonshotai: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
516
- "moonshotai-cn": ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
536
+ moonshotai: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
537
+ "moonshotai-cn": ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2-thinking-turbo", "kimi-k2-thinking", "kimi-k2.5"],
517
538
  };
518
539
 
519
540
  const ORACLE_SYSTEM_PROMPT = [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-oracle",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "An Amp-style oracle extension for pi that consults the strongest reasoning model on your current provider.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.84.2
1
+ 0.84.4
@@ -1,12 +1,13 @@
1
1
  # permission-gate
2
2
 
3
- A small pi extension that prompts for confirmation before running potentially dangerous bash commands or writing to protected paths.
3
+ A small pi extension that prompts for confirmation before running potentially dangerous bash or PowerShell commands or writing to protected paths.
4
4
 
5
- This is adapted from the original `permission-gate.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/permission-gate.ts) and kept basically the same.
5
+ This started from the original `permission-gate.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/permission-gate.ts) and adds conservative shell and path hardening.
6
6
 
7
7
  ## What it checks
8
8
 
9
9
  - `rm -rf`
10
+ - PowerShell `Remove-Item -Recurse -Force` and its standard aliases, including module-qualified, interpolated, splatted, and backtick-escaped forms
10
11
  - `sudo`
11
12
  - `chmod` / `chown` with `777`
12
13
  - direct `write` / `edit` calls touching normalized protected paths:
@@ -18,6 +19,12 @@ Safe `.env` templates/examples such as `.env.example` and `.env.production.templ
18
19
 
19
20
  If pi is running without an interactive UI, it blocks matching commands and protected path writes by default.
20
21
 
22
+ For Pi's built-in local Windows PowerShell tool, commands that pass the fast lexical checks are also parsed with the same PowerShell installation's AST parser before execution. Malformed input, parser failures, `Remove-Item` parameter splatting, command-resolution changes (aliases/modules/providers/direct function or filter definitions), known script/process/job/member invocation wrappers, computed member calls, and analyzer inputs above 16,000 UTF-8 bytes are treated conservatively and require confirmation (or are blocked when no UI is available). Computed targets passed to provider-capable mutation commands also require confirmation because the target could resolve to `Alias:` or `Function:`. Literal quoted/commented examples, including literal here-strings, stay benign. Definitely enabled `-WhatIf` removals (`-WhatIf`, `-WhatIf:$true`, or `-WhatIf:1`) remain non-destructive and are allowed; computed switch values require confirmation.
23
+
24
+ Non-Windows and custom/remote `powershell` tools receive the conservative lexical checks, but only Pi's built-in local Windows tool can be verified with its actual parser. Pi extensions are trusted code: tool overrides, remote operations, spawn hooks, and mutations made by handlers loaded after this gate are outside its boundary and must enforce their own final-input policy.
25
+
26
+ This is a targeted confirmation guard for the command classes listed above, not a shell sandbox or a proof that every unlisted executable/API is harmless.
27
+
21
28
  ## Install
22
29
 
23
30
  ### Standalone npm package
@@ -47,6 +54,7 @@ Then reload pi:
47
54
  ## Notes
48
55
 
49
56
  - Hooks the `tool_call` event.
50
- - Inspects `bash`, `write`, and `edit` tool calls.
57
+ - Inspects `bash`, `powershell`, `write`, and `edit` tool calls.
58
+ - Validates shell timeouts as well as command strings and fails closed on malformed calls.
51
59
  - Normalizes relative/absolute paths before matching so traversal tricks do not bypass the guard.
52
60
  - Prompts with a simple `Yes` / `No` selector before allowing dangerous commands or protected path writes.
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Permission Gate Extension
3
3
  *
4
- * Prompts for confirmation before running potentially dangerous bash commands
5
- * or modifying protected paths via write/edit.
4
+ * Prompts for confirmation before running potentially dangerous bash or
5
+ * PowerShell commands, or modifying protected paths via write/edit.
6
6
  * Protected paths include exact .git and node_modules path segments plus
7
7
  * secret-bearing .env files (excluding example/template variants).
8
8
  */
@@ -11,10 +11,19 @@ import * as path from "node:path";
11
11
 
12
12
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
13
 
14
+ import {
15
+ analyzePowerShellLexically,
16
+ buildPowerShellAstCommand,
17
+ MAX_POWERSHELL_ANALYSIS_SOURCE_BYTES,
18
+ parsePowerShellAstOutput,
19
+ } from "./powershell-safety.ts";
20
+
14
21
  const dangerousPatterns = [/\bsudo\b/i, /\b(chmod|chown)\b.*777/i];
15
22
  const protectedPathSegments = new Set([".git", "node_modules"]);
16
23
  const shellCommandSeparators = new Set([";", "&", "&&", "||", "|", "\n", "(", ")", "{", "}"]);
17
24
  const safeEnvSuffixes = new Set(["example", "examples", "template", "templates"]);
25
+ const MAX_SHELL_TIMEOUT_SECONDS = 2_147_483_647 / 1000;
26
+ const POWERSHELL_ANALYSIS_TIMEOUT_MS = 5_000;
18
27
 
19
28
  type GuardDecision = { block: true; reason: string } | undefined;
20
29
  type NormalizedPath = {
@@ -30,6 +39,7 @@ type ShellToken = {
30
39
 
31
40
  type WriteInput = { path: string; content: string };
32
41
  type EditInput = { path: string; edits: Array<{ oldText: string; newText: string }> };
42
+ type ShellInput = { command: string; timeout?: number };
33
43
 
34
44
  function isRecord(value: unknown): value is Record<string, unknown> {
35
45
  return !!value && typeof value === "object" && !Array.isArray(value);
@@ -524,6 +534,55 @@ function hasDangerousRecursiveRm(command: string): boolean {
524
534
  return evaluateTokens(currentCommand);
525
535
  }
526
536
 
537
+ function validateShellInput(input: unknown): ShellInput | undefined {
538
+ if (!isRecord(input) || typeof input.command !== "string" || input.command.trim() === "") return undefined;
539
+ if (
540
+ input.timeout !== undefined &&
541
+ (typeof input.timeout !== "number" ||
542
+ !Number.isFinite(input.timeout) ||
543
+ input.timeout <= 0 ||
544
+ input.timeout > MAX_SHELL_TIMEOUT_SECONDS)
545
+ ) {
546
+ return undefined;
547
+ }
548
+ return input.timeout === undefined
549
+ ? { command: input.command }
550
+ : { command: input.command, timeout: input.timeout };
551
+ }
552
+
553
+ async function hasDangerousPowerShellCommand(
554
+ pi: ExtensionAPI,
555
+ command: string,
556
+ signal?: AbortSignal,
557
+ ): Promise<boolean> {
558
+ // Keep both lexical work and Windows' base64-expanded -Command transport
559
+ // bounded below the platform command-line limit. Oversized input is
560
+ // deliberately treated as requiring confirmation rather than failing open.
561
+ if (Buffer.byteLength(command, "utf8") > MAX_POWERSHELL_ANALYSIS_SOURCE_BYTES) return true;
562
+
563
+ const lexicalAnalysis = analyzePowerShellLexically(command);
564
+ if (lexicalAnalysis.risky) return true;
565
+
566
+ // Pi only exposes its built-in PowerShell tool on Windows. On that platform,
567
+ // ask PowerShell's own parser for the final word so ordinary syntax features
568
+ // (module qualification, interpolation, splatting, and backtick escapes) cannot
569
+ // bypass the fallback lexer. Parser failures and malformed output fail closed.
570
+ if (process.platform !== "win32") return false;
571
+
572
+ try {
573
+ const { getPowerShellConfig } = await import("@earendil-works/pi-coding-agent");
574
+ const config = getPowerShellConfig();
575
+ const result = await pi.exec(config.shell, [...config.args, buildPowerShellAstCommand(command)], {
576
+ timeout: POWERSHELL_ANALYSIS_TIMEOUT_MS,
577
+ signal,
578
+ });
579
+ if (result.code !== 0 || result.killed) return true;
580
+ return parsePowerShellAstOutput(result.stdout).risky;
581
+ } catch {
582
+ return true;
583
+ }
584
+ }
585
+
527
586
  function validateWriteInput(input: unknown): WriteInput | undefined {
528
587
  if (!isRecord(input)) return undefined;
529
588
  if (typeof input.path !== "string" || typeof input.content !== "string") return undefined;
@@ -576,13 +635,17 @@ async function confirmProtectedPathAction(
576
635
 
577
636
  export default function (pi: ExtensionAPI) {
578
637
  pi.on("tool_call", async (event, ctx) => {
579
- if (event.toolName === "bash") {
580
- const command = event.input?.command;
581
- if (typeof command !== "string" || command.trim() === "") {
582
- return { block: true, reason: "Malformed bash command blocked" };
638
+ if (event.toolName === "bash" || event.toolName === "powershell") {
639
+ const shellToolName = event.toolName;
640
+ const input = validateShellInput(event.input);
641
+ if (!input) {
642
+ return { block: true, reason: `Malformed ${shellToolName} command blocked` };
583
643
  }
644
+ const { command } = input;
584
645
 
585
- const isDangerous = hasDangerousRecursiveRm(command) || dangerousPatterns.some((p) => p.test(command));
646
+ const isDangerous = shellToolName === "powershell"
647
+ ? await hasDangerousPowerShellCommand(pi, command, ctx.signal)
648
+ : hasDangerousRecursiveRm(command) || dangerousPatterns.some((pattern) => pattern.test(command));
586
649
 
587
650
  if (isDangerous) {
588
651
  if (!ctx.hasUI) {
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-permission-gate",
3
- "version": "0.1.13",
4
- "description": "A pi extension that prompts before dangerous bash commands and protected file writes.",
3
+ "version": "0.1.14",
4
+ "description": "A pi extension that prompts before dangerous shell commands and protected file writes.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi",
8
8
  "security",
9
9
  "bash",
10
+ "powershell",
10
11
  "permissions",
11
12
  "file-safety"
12
13
  ],
@@ -18,6 +19,7 @@
18
19
  },
19
20
  "files": [
20
21
  "index.ts",
22
+ "powershell-safety.ts",
21
23
  "README.md",
22
24
  ".pi-fleet-tested-version"
23
25
  ],