@fyeeme/pi-peon-ping 1.1.0 → 1.1.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.1.2] - 2026-09-09
6
+
7
+ ### Fixed
8
+
9
+ - peon.sh discovery (including the up-to-3s `brew --prefix` probe) no longer runs at extension load time; it is deferred to the first session event per the extensions doc's factory rule.
10
+ - `PermissionRequest` (input.required) now fires from `ui_prompt_start` — any blocking `ctx.ui` dialog — instead of sniffing the omp-only `ask_user_question` tool name, which does not exist in pi.
11
+ - Tab-title project name and event payloads use `ctx.cwd` instead of a stale factory-time `process.cwd()`.
12
+
13
+ ## [1.1.1] - 2026-08-08
14
+
15
+ ### Changed
16
+
17
+ - Tab 标题改用 provided API:删除手写 ANSI `setTabTitle`(`process.stdout.write(\x1b]0;...\x07)`),四处调用点(`session_start`/`before_agent_start`/`agent_settled`/`tool_result`,均在 `ctx.hasUI` 守卫下)改为 `ctx.ui.setTitle(...)`——mode 感知,print/json 模式自动 no-op,不再向 stdout 写裸 ANSI。
18
+ - `Stop` 事件映射从 `agent_end` 改为 `agent_settled`(与 ExtensionAPI 实际事件名一致)。
19
+
5
20
  ## [1.1.0] - 2026-07-26
6
21
 
7
22
  ### Added
package/README.md CHANGED
@@ -97,9 +97,9 @@ pi-peon-ping auto-discovers the installed `peon.sh` with the following priority
97
97
 
98
98
  When a `.ps1` file is found, it is automatically run with `powershell` instead of `bash`.
99
99
 
100
- ### peon-ping 配置
100
+ ### peon-ping configuration
101
101
 
102
- 所有 peon-ping 的功能配置通过 `peon` CLI 进行:
102
+ All peon-ping feature configuration is done through the `peon` CLI:
103
103
 
104
104
  ```bash
105
105
  peon packs use glados # Switch sound pack
package/index.ts CHANGED
@@ -14,8 +14,13 @@
14
14
  * Event mapping (pi ExtensionAPI → peon.sh hook_event_name):
15
15
  * session_start → SessionStart
16
16
  * turn_start → UserPromptSubmit
17
- * turn_end → Stop
17
+ * agent_settled → Stop
18
18
  * tool_result (event.isError === true) → PostToolUseFailure
19
+ * ui_prompt_start → PermissionRequest (input.required:
20
+ * any blocking ctx.ui dialog —
21
+ * replaces omp's ask_user_question
22
+ * tool_call sniff, which doesn't
23
+ * exist in pi)
19
24
  * session_shutdown → SessionEnd
20
25
  *
21
26
  * peon.sh discovery (first match wins):
@@ -131,13 +136,9 @@ export function resolveShellAndScript(
131
136
  }
132
137
 
133
138
  // ============================================================================
134
- // Terminal tab title helpers
139
+ // Terminal tab title (via pi's mode-aware ctx.ui.setTitle)
135
140
  // ============================================================================
136
141
 
137
- export function setTabTitle(title: string): void {
138
- process.stdout.write(`\x1b]0;${title}\x07`);
139
- }
140
-
141
142
  // ============================================================================
142
143
  // Session ID helpers
143
144
  // ============================================================================
@@ -301,18 +302,23 @@ export async function execPeonCli(
301
302
  // ============================================================================
302
303
 
303
304
  export default function peonPingExtension(pi: ExtensionAPI): void {
304
- const peonPath = findPeonSh();
305
- if (!peonPath) {
306
- console.warn("[pi-peon-ping] peon.sh not found. Install peon-ping first:");
307
- console.warn(" brew install PeonPing/tap/peon-ping");
308
- console.warn(" # or: curl -fsSL peonping.com/install | bash");
309
- console.warn(" # or: set PEON_SH=/path/to/peon.sh");
310
- return;
311
- }
312
-
313
- const cliPath = findPeonCli(peonPath);
314
- const cwd = process.cwd();
315
- const projectName = basename(cwd) || "pi";
305
+ // Doc (Long-lived resources): extension factories may run in invocations
306
+ // that never start a session — discovery (which may execFileSync `brew`,
307
+ // up to 3s) must not run here. Resolve lazily on first session_start and
308
+ // memoize per extension instance.
309
+ let peonPath: string | null | undefined;
310
+ const resolvePeon = (): string | null => {
311
+ if (peonPath === undefined) {
312
+ peonPath = findPeonSh();
313
+ if (!peonPath) {
314
+ console.warn("[pi-peon-ping] peon.sh not found. Install peon-ping first:");
315
+ console.warn(" brew install PeonPing/tap/peon-ping");
316
+ console.warn(" # or: curl -fsSL peonping.com/install | bash");
317
+ console.warn(" # or: set PEON_SH=/path/to/peon.sh");
318
+ }
319
+ }
320
+ return peonPath;
321
+ };
316
322
 
317
323
  // -----------------------------------------------------------------------
318
324
  // Lifecycle event forwarding (respect peon-ping category toggles)
@@ -321,65 +327,84 @@ export default function peonPingExtension(pi: ExtensionAPI): void {
321
327
  // -----------------------------------------------------------------------
322
328
 
323
329
  pi.on("session_start", async (_event, ctx) => {
324
- const config = readPeonConfig(peonPath);
330
+ const peon = resolvePeon();
331
+ if (!peon) return;
332
+ const config = readPeonConfig(peon);
325
333
  const sessionId = getSessionId(ctx);
326
- if (ctx.hasUI) setTabTitle(`● ${projectName}: ready`);
327
- firePeon(peonPath, "SessionStart", cwd, sessionId, config);
334
+ const projectName = basename(ctx.cwd) || "pi";
335
+ if (ctx.hasUI) ctx.ui.setTitle(`● ${projectName}: ready`);
336
+ firePeon(peon, "SessionStart", ctx.cwd, sessionId, config);
328
337
  });
329
338
 
330
339
  pi.on("before_agent_start", async (_event, ctx) => {
331
- const config = readPeonConfig(peonPath);
340
+ const peon = resolvePeon();
341
+ if (!peon) return;
342
+ const config = readPeonConfig(peon);
332
343
  const sessionId = getSessionId(ctx);
333
- if (ctx.hasUI) setTabTitle(`● ${projectName}: working...`);
334
- firePeon(peonPath, "UserPromptSubmit", cwd, sessionId, config);
344
+ const projectName = basename(ctx.cwd) || "pi";
345
+ if (ctx.hasUI) ctx.ui.setTitle(`● ${projectName}: working...`);
346
+ firePeon(peon, "UserPromptSubmit", ctx.cwd, sessionId, config);
335
347
  });
336
348
 
337
349
  pi.on("session_before_compact", async (_event, ctx) => {
338
- const config = readPeonConfig(peonPath);
350
+ const peon = resolvePeon();
351
+ if (!peon) return;
352
+ const config = readPeonConfig(peon);
339
353
  const sessionId = getSessionId(ctx);
340
- firePeon(peonPath, "PreCompact", cwd, sessionId, config);
354
+ firePeon(peon, "PreCompact", ctx.cwd, sessionId, config);
341
355
  });
342
356
 
343
- pi.on("agent_end", async (_event, ctx) => {
344
- const config = readPeonConfig(peonPath);
357
+ pi.on("agent_settled", async (_event, ctx) => {
358
+ const peon = resolvePeon();
359
+ if (!peon) return;
360
+ const config = readPeonConfig(peon);
345
361
  const sessionId = getSessionId(ctx);
346
- if (ctx.hasUI) setTabTitle(`✓ ${projectName}: done`);
347
- firePeon(peonPath, "Stop", cwd, sessionId, config);
362
+ const projectName = basename(ctx.cwd) || "pi";
363
+ if (ctx.hasUI) ctx.ui.setTitle(`✓ ${projectName}: done`);
364
+ firePeon(peon, "Stop", ctx.cwd, sessionId, config);
348
365
  });
349
366
 
350
367
  pi.on("tool_result", async (event, ctx) => {
351
368
  if (!event.isError) return;
352
- const config = readPeonConfig(peonPath);
369
+ const peon = resolvePeon();
370
+ if (!peon) return;
371
+ const config = readPeonConfig(peon);
353
372
  const sessionId = getSessionId(ctx);
354
- if (ctx.hasUI) setTabTitle(`✗ ${projectName}: error`);
355
- firePeon(peonPath, "PostToolUseFailure", cwd, sessionId, config);
373
+ const projectName = basename(ctx.cwd) || "pi";
374
+ if (ctx.hasUI) ctx.ui.setTitle(`✗ ${projectName}: error`);
375
+ firePeon(peon, "PostToolUseFailure", ctx.cwd, sessionId, config);
356
376
  });
357
377
 
358
- // Agent is asking the user a question (e.g. ask_user_question tool) →
359
- // peon-ping "input.required" category. Fires before the tool runs.
360
- pi.on("tool_call", async (event, ctx) => {
361
- if (event.toolName !== "ask_user_question") return;
362
- const config = readPeonConfig(peonPath);
378
+ // pi is asking the user something (any blocking ctx.ui dialog: select /
379
+ // confirm / input / editor / custom — ask_user, permission gates, …) →
380
+ // peon-ping "input.required" category. ui_prompt_start fires for every
381
+ // such span, so no per-tool sniffing is needed.
382
+ pi.on("ui_prompt_start", async (_event, ctx) => {
383
+ const peon = resolvePeon();
384
+ if (!peon) return;
385
+ const config = readPeonConfig(peon);
363
386
  const sessionId = getSessionId(ctx);
364
- firePeon(peonPath, "PermissionRequest", cwd, sessionId, config);
387
+ firePeon(peon, "PermissionRequest", ctx.cwd, sessionId, config);
365
388
  });
366
389
 
367
390
  pi.on("session_shutdown", async (_event, ctx) => {
368
- const config = readPeonConfig(peonPath);
391
+ const peon = resolvePeon();
392
+ if (!peon) return;
393
+ const config = readPeonConfig(peon);
369
394
  const sessionId = getSessionId(ctx);
370
- firePeon(peonPath, "SessionEnd", cwd, sessionId, config);
395
+ firePeon(peon, "SessionEnd", ctx.cwd, sessionId, config);
371
396
  });
372
397
 
373
398
  // -----------------------------------------------------------------------
374
399
  // Commands
375
400
  // -----------------------------------------------------------------------
376
401
 
377
- if (!cliPath) return;
378
-
379
402
  pi.registerCommand("peon-ping-toggle", {
380
403
  description: "Toggle peon-ping mute on/off",
381
404
  handler: async (_args, ctx) => {
382
- const result = await execPeonCli(cliPath, ["toggle"]);
405
+ const cli = findPeonCli(resolvePeon());
406
+ if (!cli) return;
407
+ const result = await execPeonCli(cli, ["toggle"]);
383
408
  if (result.exitCode !== 0) {
384
409
  ctx.ui.notify(result.stdout || "toggle failed", "error");
385
410
  } else {
@@ -391,12 +416,14 @@ export default function peonPingExtension(pi: ExtensionAPI): void {
391
416
  pi.registerCommand("peon-ping-use", {
392
417
  description: "Switch sound pack: /peon-ping-use <name>",
393
418
  handler: async (args, ctx) => {
419
+ const cli = findPeonCli(resolvePeon());
420
+ if (!cli) return;
394
421
  const name = args.trim();
395
422
  if (!name) {
396
423
  ctx.ui.notify("Usage: /peon-ping-use <pack-name>", "warning");
397
424
  return;
398
425
  }
399
- const result = await execPeonCli(cliPath, ["packs", "use", "--install", name]);
426
+ const result = await execPeonCli(cli, ["packs", "use", "--install", name]);
400
427
  if (result.exitCode !== 0) {
401
428
  ctx.ui.notify(result.stdout || `Failed to switch to ${name}`, "error");
402
429
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyeeme/pi-peon-ping",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "peon-ping adapter for pi — routes lifecycle events through peon.sh for sound packs, notifications, and trainer reminders.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,10 +44,10 @@
44
44
  "typecheck": "tsc"
45
45
  },
46
46
  "peerDependencies": {
47
- "@earendil-works/pi-coding-agent": ">=0.77.0"
47
+ "@earendil-works/pi-coding-agent": ">=0.84.4"
48
48
  },
49
49
  "devDependencies": {
50
- "@earendil-works/pi-coding-agent": "0.77.0",
50
+ "@earendil-works/pi-coding-agent": "0.84.4",
51
51
  "@types/node": "22.19.19",
52
52
  "typescript": "5.9.3"
53
53
  }