@diegopetrucci/pi-extensions 0.1.63 → 0.1.64

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Total npm downloads](https://img.shields.io/npm/dt/%40diegopetrucci%2Fpi-extensions?label=total%20npm%20downloads)](https://www.npmjs.com/package/@diegopetrucci/pi-extensions)
4
4
 
5
- A collection of [pi](https://pi.dev) agent extensions I made. (btw, [I am also building my own harness on top of pi](https://github.com/diegopetrucci/the-last-harness). have a look!)
5
+ A collection of [pi](https://pi.dev) agent extensions I made. (Btw, [I am also building my own harness on top of pi](https://github.com/diegopetrucci/the-last-harness). Have a look if that sounds interesting!)
6
6
 
7
7
  ### Subagents
8
8
 
@@ -13,9 +13,7 @@ A collection of [pi](https://pi.dev) agent extensions I made. (btw, [I am also b
13
13
 
14
14
  ### Providers
15
15
 
16
- - [`claude-fast`](./extensions/claude-fast): Standalone-only provider-specific alternative that adds `/claude-fast` for supported Anthropic Claude Opus models.
17
16
  - [`fast`](./extensions/fast): Adds one `/fast` toggle that enables the provider-specific Fast mode for the currently selected supported OpenAI Codex or Anthropic Claude model.
18
- - [`openai-fast`](./extensions/openai-fast): Standalone-only provider-specific alternative that adds `/fast` for ChatGPT-auth GPT-5.4, GPT-5.5, and GPT-5.6 Codex variants.
19
17
 
20
18
  ### UI
21
19
 
@@ -49,10 +47,13 @@ A collection of [pi](https://pi.dev) agent extensions I made. (btw, [I am also b
49
47
  - [`gnosis`](./extensions/gnosis): Exposes the `gn` repo-local knowledge base CLI as an agent tool for searching and recording durable project decisions, constraints, and intent.
50
48
  - [`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.
51
49
  - [`inline-bash`](./extensions/inline-bash): Expands `!{command}` snippets in user prompts by running them through bash before the prompt reaches the agent.
52
- - [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input.
50
+ - [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input, including tmux-aware terminal notifications.
53
51
  - [`todo`](./extensions/todo): Adds a branch-aware `todo` tool for the agent and a `/todos` viewer for users..
54
52
 
55
- (For the full list of pi extensions I use, [check out my dotfiles](https://github.com/diegopetrucci/dot/blob/main/.pi/agent/settings.json).)
53
+ ### Deprecated
54
+
55
+ - [`claude-fast`](./extensions/claude-fast): Standalone-only provider-specific alternative that adds `/claude-fast` for supported Anthropic Claude Opus models. [Deprecated, please use [`fast`](https://github.com/diegopetrucci/pi-extensions/blob/main/extensions/fast) instead.]
56
+ - [`openai-fast`](./extensions/openai-fast): Standalone-only provider-specific alternative that adds `/fast` for ChatGPT-auth GPT-5.4, GPT-5.5, and GPT-5.6 Codex variants. [Deprecated, please use [`fast`](https://github.com/diegopetrucci/pi-extensions/blob/main/extensions/fast) instead.]
56
57
 
57
58
  ## Install
58
59
 
@@ -11,6 +11,9 @@ This started from the original `notify.ts` example in [`earendil-works/pi`](http
11
11
  - OSC 777: Ghostty, iTerm2, WezTerm, rxvt-unicode
12
12
  - OSC 99: Kitty
13
13
 
14
+ Both are wrapped in tmux's DCS passthrough automatically when `$TMUX` is set;
15
+ see [Running inside tmux](#running-inside-tmux).
16
+
14
17
  ### Desktop notifications
15
18
 
16
19
  - macOS Notification Center via `osascript`
@@ -86,7 +89,8 @@ Example:
86
89
  "sound": false
87
90
  },
88
91
  "terminal": {
89
- "backend": "auto"
92
+ "backend": "auto",
93
+ "tmuxPassthrough": "auto"
90
94
  },
91
95
  "desktop": {
92
96
  "backend": "auto"
@@ -102,6 +106,39 @@ Example:
102
106
  }
103
107
  ```
104
108
 
109
+ ### Running inside tmux
110
+
111
+ Under tmux, terminal notifications need one line in your `tmux.conf`:
112
+
113
+ ```tmux
114
+ set -g allow-passthrough all
115
+ ```
116
+
117
+ Without it, notifications never reach your terminal. tmux is the terminal
118
+ emulator for the pane, so the escape sequence is delivered to tmux rather than
119
+ to the terminal that can act on it. tmux has no OSC 777 or OSC 99 handler
120
+ (checked in 3.7b, whose OSC dispatch covers 4/7/8/9;4/10/11/12/52/104/133) and
121
+ does not forward OSC codes it does not recognise, so the notification is parsed
122
+ and silently dropped. Desktop, bell, and sound channels are unaffected.
123
+
124
+ This extension therefore wraps terminal notifications in tmux's DCS passthrough
125
+ (`ESC P tmux ; … ESC \`), which asks tmux to forward the payload to the outer
126
+ terminal verbatim — but tmux only honours that when `allow-passthrough` is on.
127
+
128
+ Use `all` rather than `on`: under `on`, tmux only honours passthrough from panes
129
+ that are currently *visible*, which discards exactly the notification you want —
130
+ the one fired while you are looking at another window. The trade-off is that
131
+ `all` lets a program in any pane write bytes straight to the attached terminal;
132
+ notification sequences render nothing, but a malformed passthrough from a
133
+ background pane can garble the display, since those bytes bypass tmux's screen
134
+ model.
135
+
136
+ Set `terminal.tmuxPassthrough` to `never` to opt out of wrapping (for example if
137
+ your multiplexer forwards these codes itself), or `always` to force it when
138
+ `$TMUX` is not visible in the environment. Wrapping is safe to leave on `auto`:
139
+ with passthrough disabled in tmux, a wrapped sequence is dropped exactly like a
140
+ bare one, so nothing regresses.
141
+
105
142
  ### Enable sound
106
143
 
107
144
  Minimal example:
@@ -127,6 +164,7 @@ You can also customize the sound backend and options if needed.
127
164
  - `channels.bell`: enable terminal bell
128
165
  - `channels.sound`: enable sound playback
129
166
  - `terminal.backend`: `auto`, `osc777`, `osc99`, `none`
167
+ - `terminal.tmuxPassthrough`: `auto` (wrap when `$TMUX` is set), `always`, `never`
130
168
  - `desktop.backend`: `auto`, `macos`, `linux`, `windows-toast`, `none`
131
169
  - `sound.backend`: `auto`, `macos`, `linux`, `windows-beep`, `command`, `none`
132
170
  - `sound.name`: macOS system sound name, like `Glass` or `Hero`
@@ -138,6 +176,7 @@ You can also customize the sound backend and options if needed.
138
176
  ## Notes
139
177
 
140
178
  - Hooks the `agent_settled` event so automatic retries, compaction retries, and queued follow-ups do not trigger intermediate notifications.
179
+ - Terminal notifications are wrapped for tmux when `$TMUX` is set, which also requires `set -g allow-passthrough all` in `tmux.conf`.
141
180
  - Default message is `Pi` / `Ready for input`.
142
181
  - Terminal, desktop, bell, and sound channels can be enabled independently.
143
182
  - To opt into sound playback, set `channels.sound` to `true`.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Sends notifications when Pi agent is done and waiting for input.
5
5
  * Supports multiple channels:
6
- * - terminal notifications: OSC 777 and OSC 99
6
+ * - terminal notifications: OSC 777 and OSC 99, wrapped for tmux when needed
7
7
  * - desktop notifications: macOS Notification Center, Linux notify-send, Windows toast
8
8
  * - terminal bell
9
9
  * - sound playback
@@ -20,6 +20,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
20
20
  import { CONFIG_DIR_NAME, getAgentDir } from "@earendil-works/pi-coding-agent";
21
21
 
22
22
  type TerminalBackend = "auto" | "osc777" | "osc99" | "none";
23
+ type TmuxPassthrough = "auto" | "always" | "never";
23
24
  type DesktopBackend = "auto" | "macos" | "linux" | "windows-toast" | "none";
24
25
  type SoundBackend = "auto" | "macos" | "linux" | "windows-beep" | "command" | "none";
25
26
 
@@ -41,6 +42,7 @@ interface NotifyConfig {
41
42
  };
42
43
  terminal: {
43
44
  backend: TerminalBackend;
45
+ tmuxPassthrough: TmuxPassthrough;
44
46
  };
45
47
  desktop: {
46
48
  backend: DesktopBackend;
@@ -68,6 +70,7 @@ const DEFAULT_CONFIG: NotifyConfig = {
68
70
  },
69
71
  terminal: {
70
72
  backend: "auto",
73
+ tmuxPassthrough: "auto",
71
74
  },
72
75
  desktop: {
73
76
  backend: "auto",
@@ -145,13 +148,47 @@ function windowsToastScript(title: string, body: string): string {
145
148
  ].join("; ");
146
149
  }
147
150
 
148
- function notifyOSC777(title: string, body: string): void {
149
- process.stdout.write(`\x1b]777;notify;${title};${body}\x07`);
151
+ /**
152
+ * Inside tmux, terminal notifications are written to tmux, not to the terminal
153
+ * that can act on them: tmux is the pane's emulator and parses the sequence
154
+ * itself. tmux has no OSC 777 or OSC 99 handler (checked in 3.7b, whose OSC
155
+ * dispatch covers 4/7/8/9;4/10/11/12/52/104/133) and does not forward OSC codes
156
+ * it does not recognise, so both backends are silently dropped.
157
+ *
158
+ * tmux's DCS passthrough asks it to write a payload to the outer terminal
159
+ * verbatim. Every ESC inside the payload must be doubled.
160
+ *
161
+ * Users must also allow passthrough in tmux.conf:
162
+ *
163
+ * set -g allow-passthrough all
164
+ *
165
+ * `all` rather than `on`, because `on` only honours passthrough from panes that
166
+ * are currently visible - which drops exactly the notification that matters,
167
+ * the one fired while the user is looking at a different window. When
168
+ * passthrough is disabled entirely, the wrapped sequence is dropped just like
169
+ * the bare one, so wrapping is never a regression.
170
+ */
171
+ function wrapForTmux(sequence: string): string {
172
+ return `\x1bPtmux;${sequence.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
173
+ }
174
+
175
+ function shouldWrapForTmux(config: NotifyConfig): boolean {
176
+ if (config.terminal.tmuxPassthrough === "never") return false;
177
+ if (config.terminal.tmuxPassthrough === "always") return true;
178
+ return Boolean(process.env.TMUX);
179
+ }
180
+
181
+ function writeTerminalSequence(sequence: string, wrap: boolean): void {
182
+ process.stdout.write(wrap ? wrapForTmux(sequence) : sequence);
150
183
  }
151
184
 
152
- function notifyOSC99(title: string, body: string): void {
153
- process.stdout.write(`\x1b]99;i=1:d=0;${title}\x1b\\`);
154
- process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`);
185
+ function notifyOSC777(title: string, body: string, wrap: boolean): void {
186
+ writeTerminalSequence(`\x1b]777;notify;${title};${body}\x07`, wrap);
187
+ }
188
+
189
+ function notifyOSC99(title: string, body: string, wrap: boolean): void {
190
+ writeTerminalSequence(`\x1b]99;i=1:d=0;${title}\x1b\\`, wrap);
191
+ writeTerminalSequence(`\x1b]99;i=1:p=body;${body}\x1b\\`, wrap);
155
192
  }
156
193
 
157
194
  function ringBell(): void {
@@ -195,13 +232,18 @@ function detectSoundBackend(config: NotifyConfig): Exclude<SoundBackend, "auto">
195
232
  return "none";
196
233
  }
197
234
 
198
- function sendTerminalNotification(title: string, body: string, backend: Exclude<TerminalBackend, "auto">): void {
235
+ function sendTerminalNotification(
236
+ title: string,
237
+ body: string,
238
+ backend: Exclude<TerminalBackend, "auto">,
239
+ wrap: boolean,
240
+ ): void {
199
241
  if (backend === "osc99") {
200
- notifyOSC99(title, body);
242
+ notifyOSC99(title, body, wrap);
201
243
  return;
202
244
  }
203
245
  if (backend === "osc777") {
204
- notifyOSC777(title, body);
246
+ notifyOSC777(title, body, wrap);
205
247
  }
206
248
  }
207
249
 
@@ -263,7 +305,12 @@ export default function notifyExtension(pi: ExtensionAPI) {
263
305
  const tasks: Array<Promise<unknown>> = [];
264
306
 
265
307
  if (config.channels.terminal) {
266
- sendTerminalNotification(config.title, config.body, detectTerminalBackend(config));
308
+ sendTerminalNotification(
309
+ config.title,
310
+ config.body,
311
+ detectTerminalBackend(config),
312
+ shouldWrapForTmux(config),
313
+ );
267
314
  }
268
315
 
269
316
  if (config.channels.desktop) {
@@ -10,7 +10,8 @@
10
10
  "sound": false
11
11
  },
12
12
  "terminal": {
13
- "backend": "auto"
13
+ "backend": "auto",
14
+ "tmuxPassthrough": "auto"
14
15
  },
15
16
  "desktop": {
16
17
  "backend": "auto"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-notify",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "A pi extension that sends a notification when the agent is ready for input.",
5
5
  "keywords": [
6
6
  "pi-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-extensions",
3
- "version": "0.1.63",
3
+ "version": "0.1.64",
4
4
  "description": "A collection of pi extensions and skills for annotation UIs, context management, workflow audits, contrarian review, 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",