@fyeeme/pi-peon-ping 1.1.1 → 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.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/index.ts +65 -34
  3. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
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
+
5
13
  ## [1.1.1] - 2026-08-08
6
14
 
7
15
  ### Changed
package/index.ts CHANGED
@@ -16,6 +16,11 @@
16
16
  * turn_start → UserPromptSubmit
17
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):
@@ -297,18 +302,23 @@ export async function execPeonCli(
297
302
  // ============================================================================
298
303
 
299
304
  export default function peonPingExtension(pi: ExtensionAPI): void {
300
- const peonPath = findPeonSh();
301
- if (!peonPath) {
302
- console.warn("[pi-peon-ping] peon.sh not found. Install peon-ping first:");
303
- console.warn(" brew install PeonPing/tap/peon-ping");
304
- console.warn(" # or: curl -fsSL peonping.com/install | bash");
305
- console.warn(" # or: set PEON_SH=/path/to/peon.sh");
306
- return;
307
- }
308
-
309
- const cliPath = findPeonCli(peonPath);
310
- const cwd = process.cwd();
311
- 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
+ };
312
322
 
313
323
  // -----------------------------------------------------------------------
314
324
  // Lifecycle event forwarding (respect peon-ping category toggles)
@@ -317,65 +327,84 @@ export default function peonPingExtension(pi: ExtensionAPI): void {
317
327
  // -----------------------------------------------------------------------
318
328
 
319
329
  pi.on("session_start", async (_event, ctx) => {
320
- const config = readPeonConfig(peonPath);
330
+ const peon = resolvePeon();
331
+ if (!peon) return;
332
+ const config = readPeonConfig(peon);
321
333
  const sessionId = getSessionId(ctx);
334
+ const projectName = basename(ctx.cwd) || "pi";
322
335
  if (ctx.hasUI) ctx.ui.setTitle(`● ${projectName}: ready`);
323
- firePeon(peonPath, "SessionStart", cwd, sessionId, config);
336
+ firePeon(peon, "SessionStart", ctx.cwd, sessionId, config);
324
337
  });
325
338
 
326
339
  pi.on("before_agent_start", async (_event, ctx) => {
327
- const config = readPeonConfig(peonPath);
340
+ const peon = resolvePeon();
341
+ if (!peon) return;
342
+ const config = readPeonConfig(peon);
328
343
  const sessionId = getSessionId(ctx);
344
+ const projectName = basename(ctx.cwd) || "pi";
329
345
  if (ctx.hasUI) ctx.ui.setTitle(`● ${projectName}: working...`);
330
- firePeon(peonPath, "UserPromptSubmit", cwd, sessionId, config);
346
+ firePeon(peon, "UserPromptSubmit", ctx.cwd, sessionId, config);
331
347
  });
332
348
 
333
349
  pi.on("session_before_compact", async (_event, ctx) => {
334
- const config = readPeonConfig(peonPath);
350
+ const peon = resolvePeon();
351
+ if (!peon) return;
352
+ const config = readPeonConfig(peon);
335
353
  const sessionId = getSessionId(ctx);
336
- firePeon(peonPath, "PreCompact", cwd, sessionId, config);
354
+ firePeon(peon, "PreCompact", ctx.cwd, sessionId, config);
337
355
  });
338
356
 
339
357
  pi.on("agent_settled", async (_event, ctx) => {
340
- const config = readPeonConfig(peonPath);
358
+ const peon = resolvePeon();
359
+ if (!peon) return;
360
+ const config = readPeonConfig(peon);
341
361
  const sessionId = getSessionId(ctx);
362
+ const projectName = basename(ctx.cwd) || "pi";
342
363
  if (ctx.hasUI) ctx.ui.setTitle(`✓ ${projectName}: done`);
343
- firePeon(peonPath, "Stop", cwd, sessionId, config);
364
+ firePeon(peon, "Stop", ctx.cwd, sessionId, config);
344
365
  });
345
366
 
346
367
  pi.on("tool_result", async (event, ctx) => {
347
368
  if (!event.isError) return;
348
- const config = readPeonConfig(peonPath);
369
+ const peon = resolvePeon();
370
+ if (!peon) return;
371
+ const config = readPeonConfig(peon);
349
372
  const sessionId = getSessionId(ctx);
373
+ const projectName = basename(ctx.cwd) || "pi";
350
374
  if (ctx.hasUI) ctx.ui.setTitle(`✗ ${projectName}: error`);
351
- firePeon(peonPath, "PostToolUseFailure", cwd, sessionId, config);
375
+ firePeon(peon, "PostToolUseFailure", ctx.cwd, sessionId, config);
352
376
  });
353
377
 
354
- // Agent is asking the user a question (e.g. ask_user_question tool) →
355
- // peon-ping "input.required" category. Fires before the tool runs.
356
- pi.on("tool_call", async (event, ctx) => {
357
- if (event.toolName !== "ask_user_question") return;
358
- 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);
359
386
  const sessionId = getSessionId(ctx);
360
- firePeon(peonPath, "PermissionRequest", cwd, sessionId, config);
387
+ firePeon(peon, "PermissionRequest", ctx.cwd, sessionId, config);
361
388
  });
362
389
 
363
390
  pi.on("session_shutdown", async (_event, ctx) => {
364
- const config = readPeonConfig(peonPath);
391
+ const peon = resolvePeon();
392
+ if (!peon) return;
393
+ const config = readPeonConfig(peon);
365
394
  const sessionId = getSessionId(ctx);
366
- firePeon(peonPath, "SessionEnd", cwd, sessionId, config);
395
+ firePeon(peon, "SessionEnd", ctx.cwd, sessionId, config);
367
396
  });
368
397
 
369
398
  // -----------------------------------------------------------------------
370
399
  // Commands
371
400
  // -----------------------------------------------------------------------
372
401
 
373
- if (!cliPath) return;
374
-
375
402
  pi.registerCommand("peon-ping-toggle", {
376
403
  description: "Toggle peon-ping mute on/off",
377
404
  handler: async (_args, ctx) => {
378
- const result = await execPeonCli(cliPath, ["toggle"]);
405
+ const cli = findPeonCli(resolvePeon());
406
+ if (!cli) return;
407
+ const result = await execPeonCli(cli, ["toggle"]);
379
408
  if (result.exitCode !== 0) {
380
409
  ctx.ui.notify(result.stdout || "toggle failed", "error");
381
410
  } else {
@@ -387,12 +416,14 @@ export default function peonPingExtension(pi: ExtensionAPI): void {
387
416
  pi.registerCommand("peon-ping-use", {
388
417
  description: "Switch sound pack: /peon-ping-use <name>",
389
418
  handler: async (args, ctx) => {
419
+ const cli = findPeonCli(resolvePeon());
420
+ if (!cli) return;
390
421
  const name = args.trim();
391
422
  if (!name) {
392
423
  ctx.ui.notify("Usage: /peon-ping-use <pack-name>", "warning");
393
424
  return;
394
425
  }
395
- const result = await execPeonCli(cliPath, ["packs", "use", "--install", name]);
426
+ const result = await execPeonCli(cli, ["packs", "use", "--install", name]);
396
427
  if (result.exitCode !== 0) {
397
428
  ctx.ui.notify(result.stdout || `Failed to switch to ${name}`, "error");
398
429
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fyeeme/pi-peon-ping",
3
- "version": "1.1.1",
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.84.1"
47
+ "@earendil-works/pi-coding-agent": ">=0.84.4"
48
48
  },
49
49
  "devDependencies": {
50
- "@earendil-works/pi-coding-agent": "0.84.1",
50
+ "@earendil-works/pi-coding-agent": "0.84.4",
51
51
  "@types/node": "22.19.19",
52
52
  "typescript": "5.9.3"
53
53
  }