pi-usereq 0.86.0 → 0.87.0

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
@@ -1,4 +1,4 @@
1
- # PI-useReq/pi-usereq (0.86.0)
1
+ # PI-useReq/pi-usereq (0.87.0)
2
2
 
3
3
  <p align="center">
4
4
  <img src="https://img.shields.io/badge/python-3.11%2B-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python 3.11+">
@@ -60,7 +60,7 @@ in sync with the repository. All capabilities are exposed as slash commands and
60
60
  - **11 built-in agent tools** - token counting, summarization, compression, construct search, and static-check tools for explicit files or for the configured project surface.
61
61
  - **Interactive configuration menu** - `/pi-usereq` manages local (`<base-path>/.pi-usereq.json`) and global (`~/.config/pi-usereq/config.json`) configuration with no manual JSON editing required.
62
62
  - **Worktree-isolated runs** - prompt-command executions run in an isolated git worktree (created with `<prefix><project>-<branch>-<YYYYMMDDHHMMSS>` names) and are merged back with a stash-assisted fast-forward on success.
63
- - **Automatic commit guidance** - `AUTO_GIT_COMMIT=enable` (default) injects structured commit instructions (`<TYPE>(<COMPONENT>): <DESCRIPTION> [useReq]`) into every prompt; disabling it forces read-only git behavior and turns worktree orchestration off.
63
+ - **Automatic commit guidance** - `AUTO_GIT_COMMIT=enable` (default) injects structured commit instructions (`<TYPE>(<COMPONENT>)<BREAKING>: <DESCRIPTION> [useReq]`) into every prompt; disabling it forces read-only git behavior and turns worktree orchestration off.
64
64
  - **Notifications** - desktop notify command, sound effects (levels `none`/`low`/`mid`/`high`, default `alt+s` toggle) and Pushover push messages on prompt completion, interruption, or failure.
65
65
  - **Runtime status bar** - the extension renders its workflow state, current branch, context usage, elapsed time, and sound level in the pi status line.
66
66
  - **Context-occupancy guardrails** - the `Context Files` settings row and every command invocation summary show the measured `[<percent>% context]` occupancy of the enabled context files against the selected model's max input context (or the documented 1,000,000-token fallback); prompt dispatch is aborted before worktree creation when the enabled context files exceed that limit.
@@ -72,7 +72,9 @@ in sync with the repository. All capabilities are exposed as slash commands and
72
72
  Each `req-*` command is invoked as `/req-<name> <User Request>` inside pi. Prompt commands first run git validation and the
73
73
  prompt-specific required-document checks, then (when `Auto git commit` and `Git worktree` are enabled) switch the session into an
74
74
  isolated worktree, render the bundled prompt with the project context, and on success merge the changes back and leave the
75
- repository clean. `/req-references` and `/req-reset` are non-agentic: they execute directly without starting an LLM session or
75
+ repository clean. Every command prints an on-screen invocation summary (command name, passed arguments, and active
76
+ configuration); the full rendered prompt content is delivered only to the LLM agent and is never displayed on screen.
77
+ `/req-references` and `/req-reset` are non-agentic: they execute directly without starting an LLM session or
76
78
  creating a worktree.
77
79
 
78
80
  > **Clean context between commands** — every `/req-*` orchestration keeps the session context content in place when it
@@ -320,19 +322,19 @@ The extension renders a status field in the pi status line showing: extension id
320
322
  - Config-gated slash commands that run the project tools and write the output into the editor: `debug-compress`, `debug-references`, `debug-static-check`, `debug-summarize`, `debug-tokens`.
321
323
  - Standalone debug harness: `scripts/pi-usereq-debug.sh` (bash wrapper) and `scripts/debug-extension.ts` with subcommands `inspect`, `session-start`, `command`, `tool`, and `sdk-smoke`.
322
324
  - Debug logging writes to the configured `DEBUG_LOG_FILE`, filtered by prompt/tool name and workflow status.
323
- - **Prompt content logging** — when both `Debug` and `Enable debug prompts` are enabled (the `Debug > Enable debug prompts` menu entry), the exact rendered prompt delivered to the LLM agent through `sendMessage`/`sendUserMessage` is saved under `Log path for prompts` (`DEBUG_PROMPTS_LOG_PATH`, default `/tmp/PI-useReq`). Each run writes one file named `<timestamp>-<command>` (a `YYYYMMDDHHMMSSmmm` timestamp plus the `/req-*` command used, e.g. `20261009072235123-req-implement`) whose content is byte-identical to what reached the model. Prompt logging is subordinate to the global `Debug` flag and to the `Log on status` workflow filter; nothing is written while `Debug` is disabled. Relative log paths resolve against the project root, and trailing slashes are stripped from the configured path.
325
+ - **Prompt content logging** — when both `Debug` and `Enable debug prompts` are enabled (the `Debug > Enable debug prompts` menu entry), the exact rendered prompt delivered to the LLM agent through `sendMessage`/`sendUserMessage` is saved under `Log path for prompts` (`DEBUG_PROMPTS_LOG_PATH`, default `/tmp/PI-useReq`). Each run writes two files named `<timestamp>-<command>-request.md` and `<timestamp>-<command>-prompt.md` (a `YYYYMMDDHHMMSSmmm` write-time timestamp plus the `/req-*` command used, e.g. `20261009072235123-req-implement-request.md` and `20261009072235123-req-implement-prompt.md`). The `-prompt.md` file is byte-identical to the rendered prompt delivered to the model; the `-request.md` file contains the exact on-screen command invocation summary. Prompt logging depends only on the global `Debug` flag plus `Enable debug prompts` (it is exempt from the `Log on status` workflow filter); nothing is written while `Debug` is disabled. Relative log paths resolve against the project root, and trailing slashes are stripped from the configured path.
324
326
 
325
327
  Because the saved file contains exactly what pi-usereq sent, you can replicate the same send from the `pi` CLI, outside pi-usereq and without the extension installed:
326
328
 
327
329
  ```bash
328
330
  # Replay the exact saved prompt as a one-shot task argument
329
- pi -p "$(cat /tmp/PI-useReq/20261009072235123-req-implement)"
331
+ pi -p "$(cat /tmp/PI-useReq/20261009072235123-req-implement-prompt.md)"
330
332
 
331
333
  # Feed extra material on stdin while replaying an analysis request
332
334
  cat errors.log | pi -p "Analyze these errors and suggest how to fix them"
333
335
 
334
336
  # Reuse a saved prompt as coding-style guidance through an appended system prompt
335
- pi -p "Rewrite this function" --append-system-prompt /tmp/PI-useReq/20261009072235123-req-implement
337
+ pi -p "Rewrite this function" --append-system-prompt /tmp/PI-useReq/20261009072235123-req-implement-prompt.md
336
338
  ```
337
339
 
338
340
  - **Provider error logging** — when a dispatched `req-*` run receives a provider response with status >= 400, pi-usereq writes the exact failing request payload into `<timestamp>-<command>-error-<status>.json` (raw provider-request JSON), the provider error message text into `<timestamp>-<command>-error-<status>-response.txt`, a byte-for-byte snapshot of the retained execution session into `<timestamp>-<command>-error-<status>-session.jsonl`, and replay metadata into `<timestamp>-<command>-error-<status>-replay.json`, all beside the run's `-request.md` and `-prompt.md` files. The `-replay.json` sidecar carries the provider, base URL, model id, thinking level, context window, max output tokens, the retained execution-session file, and ready-to-use replay commands; `replayCommands.piFork` targets the log-local `-session.jsonl` snapshot, which freezes the transcript exactly at the failing provider request (before any context edit or compaction). You can re-generate the failure for immediate pi CLI debugging:
@@ -373,7 +375,7 @@ This section describes the Git behavior of the `req-*` commands. The commands ow
373
375
  - On success the extension restores the original `base-path` session, applies a stash-assisted fast-forward merge of the worktree branch, and deletes the worktree and branch.
374
376
  - On failure or interruption the worktree and branch are **kept** and the workflow is parked in the `error` state so the produced artifacts can be inspected or recovered: run `/req-reset` to restore the original base path and force-remove the generated worktrees and branches.
375
377
  - With `Auto git commit = enable` (default), every prompt receives structured commit instructions and commits follow the message template `<TYPE>(<COMPONENT>)<BREAKING>: <DESCRIPTION> [useReq]`. With `Auto git commit = disable`, prompts receive a read-only git restriction and worktree orchestration is forced off.
376
- - The extension never rewrites history and never runs destructive cleanup on your behalf.
378
+ - The extension never rewrites git history; destructive cleanup is limited to the generated worktrees and branches, which only `/req-reset` force-removes (see above).
377
379
 
378
380
  - Recommended practice:
379
381
  - Review the changes produced by each command before pushing them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-usereq",
3
- "version": "0.86.0",
3
+ "version": "0.87.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Ogekuri/PI-useReq.git"
@@ -85,6 +85,20 @@ export const DEFAULT_PI_NOTIFY_PUSHOVER_TITLE = "%%PROMT%% @ %%BASE%% [%%TIME%%]
85
85
  */
86
86
  export const DEFAULT_PI_NOTIFY_PUSHOVER_TEXT = "%%RESULT%%\n%%ARGS%%";
87
87
 
88
+ /**
89
+ * @brief Defines the Pushover Message API `message` field limit in characters.
90
+ * @details Caps substituted Pushover bodies per REQ-445 so over-limit payloads are head-truncated instead of rejected by the provider. Access complexity is O(1).
91
+ * @satisfies REQ-445
92
+ */
93
+ export const PI_NOTIFY_PUSHOVER_MESSAGE_LIMIT = 1024;
94
+
95
+ /**
96
+ * @brief Defines the deterministic head-truncation marker for over-limit Pushover bodies.
97
+ * @details Appended after the retained head characters whenever the substituted Pushover body exceeds `PI_NOTIFY_PUSHOVER_MESSAGE_LIMIT`. Access complexity is O(1).
98
+ * @satisfies REQ-445
99
+ */
100
+ const PI_NOTIFY_PUSHOVER_TRUNCATION_MARKER = "...[truncated]";
101
+
88
102
  /**
89
103
  * @brief Enumerates supported Pushover priorities.
90
104
  * @details Restricts persisted Pushover delivery to the canonical normal and high-priority values accepted by the configuration menu. Access complexity is O(1).
@@ -99,13 +113,15 @@ export type PiNotifyPushoverPriority = (typeof PI_NOTIFY_PUSHOVER_PRIORITIES)[nu
99
113
 
100
114
  /**
101
115
  * @brief Describes one prompt-end request used for command-notify and Pushover substitution.
102
- * @details Stores the prompt command name, raw prompt arguments, runtime base path, and final prompt duration required to resolve runtime placeholders. The interface is compile-time only and introduces no runtime cost.
116
+ * @details Stores the prompt command name, raw prompt arguments, runtime base path, final prompt duration, and optional final agent output required to resolve runtime placeholders. `agentOutput` stays optional so non-agentic command callers and offline replays omit it and `%%OUTPUT%%` degrades to the empty string per REQ-444. The interface is compile-time only and introduces no runtime cost.
103
117
  */
104
118
  export interface PiNotifyEventRequest {
105
119
  promptName: string;
106
120
  promptArgs: string;
107
121
  basePath: string;
108
122
  completionTimeMs: number;
123
+ /** Final LLM agent output printed in the pi CLI; empty when absent per REQ-443, REQ-444. */
124
+ agentOutput?: string;
109
125
  }
110
126
 
111
127
  /**
@@ -490,10 +506,11 @@ function formatPiNotifyResult(outcome: PiNotifyOutcome): string {
490
506
 
491
507
  /**
492
508
  * @brief Builds the raw runtime placeholder map for one prompt-end request.
493
- * @details Resolves every placeholder value exactly once so notify-command and Pushover template substitution reuse the same prompt name, base path, elapsed time, argument string, and terminal outcome token. Runtime is O(p) in path length. No external state is mutated.
509
+ * @details Resolves every placeholder value exactly once so notify-command and Pushover template substitution reuse the same prompt name, base path, elapsed time, argument string, final agent output, and terminal outcome token. Runtime is O(p) in path length. No external state is mutated.
494
510
  * @param[in] request {PiNotifyEventRequest} Prompt-end request metadata.
495
511
  * @param[in] outcome {PiNotifyOutcome} Classified prompt-end outcome.
496
- * @return {{ "%%PROMT%%": string; "%%BASE%%": string; "%%TIME%%": string; "%%ARGS%%": string; "%%RESULT%%": string }} Raw placeholder-value map.
512
+ * @return {{ "%%PROMT%%": string; "%%BASE%%": string; "%%TIME%%": string; "%%ARGS%%": string; "%%OUTPUT%%": string; "%%RESULT%%": string }} Raw placeholder-value map.
513
+ * @satisfies REQ-169, REQ-186, REQ-443, REQ-444
497
514
  */
498
515
  function buildPiNotifyRuntimeTemplateValues(
499
516
  request: PiNotifyEventRequest,
@@ -504,6 +521,7 @@ function buildPiNotifyRuntimeTemplateValues(
504
521
  "%%BASE%%": formatPiNotifyBasePath(request.basePath),
505
522
  "%%TIME%%": formatPiNotifyDuration(request.completionTimeMs),
506
523
  "%%ARGS%%": request.promptArgs,
524
+ "%%OUTPUT%%": request.agentOutput ?? "",
507
525
  "%%RESULT%%": formatPiNotifyResult(outcome),
508
526
  };
509
527
  }
@@ -555,13 +573,13 @@ export function substitutePiNotifyInstallPath(command: string, installationPath:
555
573
 
556
574
  /**
557
575
  * @brief Substitutes runtime placeholders inside one shell-command template.
558
- * @details Applies shell-quoted installation-path substitution plus shell-escaped prompt, base-path, elapsed-time, raw-argument, and outcome-result substitution expected by `PI_NOTIFY_CMD`. Runtime is O(n) in template length. No external state is mutated.
576
+ * @details Applies shell-quoted installation-path substitution plus shell-escaped prompt, base-path, elapsed-time, raw-argument, final-output, and outcome-result substitution expected by `PI_NOTIFY_CMD`. Runtime is O(n) in template length. No external state is mutated.
559
577
  * @param[in] template {string} Raw shell-command template.
560
578
  * @param[in] request {PiNotifyEventRequest} Prompt-end request metadata.
561
579
  * @param[in] outcome {PiNotifyOutcome} Classified prompt-end outcome.
562
580
  * @param[in] installationPath {string} Absolute extension installation path.
563
581
  * @return {string} Runtime-ready shell command.
564
- * @satisfies REQ-169, REQ-199
582
+ * @satisfies REQ-169, REQ-199, REQ-443, REQ-444
565
583
  */
566
584
  function substitutePiNotifyShellTemplate(
567
585
  template: string,
@@ -579,12 +597,12 @@ function substitutePiNotifyShellTemplate(
579
597
 
580
598
  /**
581
599
  * @brief Substitutes runtime placeholders inside one text template.
582
- * @details Applies raw prompt name, home-relative base path, elapsed time, raw prompt arguments, and terminal outcome text without shell escaping so Pushover payloads preserve literal text. Runtime is O(n) in template length. No external state is mutated.
600
+ * @details Applies raw prompt name, home-relative base path, elapsed time, raw prompt arguments, final agent output, and terminal outcome text without shell escaping so Pushover payloads preserve literal text. Runtime is O(n) in template length. No external state is mutated.
583
601
  * @param[in] template {string} Raw text template.
584
602
  * @param[in] request {PiNotifyEventRequest} Prompt-end request metadata.
585
603
  * @param[in] outcome {PiNotifyOutcome} Classified prompt-end outcome.
586
604
  * @return {string} Placeholder-resolved text string.
587
- * @satisfies REQ-186, REQ-187, REQ-199
605
+ * @satisfies REQ-186, REQ-187, REQ-199, REQ-443, REQ-444
588
606
  */
589
607
  function substitutePiNotifyTextTemplate(
590
608
  template: string,
@@ -766,21 +784,38 @@ function buildPiNotifyPushoverTitle(
766
784
  return substitutePiNotifyTextTemplate(config["notify-pushover-title"], request, outcome);
767
785
  }
768
786
 
787
+ /**
788
+ * @brief Head-truncates one substituted Pushover body to the Pushover Message API limit.
789
+ * @details Returns the body unchanged when it fits `PI_NOTIFY_PUSHOVER_MESSAGE_LIMIT` and otherwise keeps the head characters that leave room for `PI_NOTIFY_PUSHOVER_TRUNCATION_MARKER` so over-limit bodies stay deterministic and provider-acceptable. Runtime is O(1). No external state is mutated.
790
+ * @param[in] body {string} Fully substituted Pushover body text.
791
+ * @return {string} Body within the Pushover `message` character limit.
792
+ * @satisfies REQ-445
793
+ */
794
+ function truncatePiNotifyPushoverBody(body: string): string {
795
+ if (body.length <= PI_NOTIFY_PUSHOVER_MESSAGE_LIMIT) {
796
+ return body;
797
+ }
798
+ return body.slice(0, PI_NOTIFY_PUSHOVER_MESSAGE_LIMIT - PI_NOTIFY_PUSHOVER_TRUNCATION_MARKER.length)
799
+ + PI_NOTIFY_PUSHOVER_TRUNCATION_MARKER;
800
+ }
801
+
769
802
  /**
770
803
  * @brief Builds the Pushover message body for one prompt-end request.
771
- * @details Resolves the configured `notify-pushover-text` template with raw runtime placeholder substitution so the pushed text remains configurable and deterministic. Runtime is O(n) in template length. No external state is mutated.
804
+ * @details Resolves the configured `notify-pushover-text` template with raw runtime placeholder substitution so the pushed text remains configurable and deterministic, then head-truncates over-limit bodies to the Pushover Message API `message` limit. Runtime is O(n) in template length. No external state is mutated.
772
805
  * @param[in] config {PiNotifyConfigFields} Effective notification configuration.
773
806
  * @param[in] outcome {PiNotifyOutcome} Classified prompt-end outcome.
774
807
  * @param[in] request {PiNotifyEventRequest} Prompt-end request metadata.
775
- * @return {string} Pushover message body.
776
- * @satisfies REQ-185, REQ-186, REQ-187, REQ-199
808
+ * @return {string} Pushover message body within the 1024-character API limit.
809
+ * @satisfies REQ-185, REQ-186, REQ-187, REQ-199, REQ-443, REQ-444, REQ-445
777
810
  */
778
811
  function buildPiNotifyPushoverBody(
779
812
  config: PiNotifyConfigFields,
780
813
  outcome: PiNotifyOutcome,
781
814
  request: PiNotifyEventRequest,
782
815
  ): string {
783
- return substitutePiNotifyTextTemplate(config["notify-pushover-text"], request, outcome);
816
+ return truncatePiNotifyPushoverBody(
817
+ substitutePiNotifyTextTemplate(config["notify-pushover-text"], request, outcome),
818
+ );
784
819
  }
785
820
 
786
821
  /**
@@ -790,7 +825,7 @@ function buildPiNotifyPushoverBody(
790
825
  * @param[in] outcome {PiNotifyOutcome} Classified prompt-end outcome.
791
826
  * @param[in] request {PiNotifyEventRequest} Prompt-end request metadata.
792
827
  * @return {URLSearchParams} Encoded Pushover request payload.
793
- * @satisfies REQ-167, REQ-172, REQ-185, REQ-186, REQ-199
828
+ * @satisfies REQ-167, REQ-172, REQ-185, REQ-186, REQ-199, REQ-443, REQ-444, REQ-445
794
829
  */
795
830
  function buildPiNotifyPushoverPayload(
796
831
  config: PiNotifyConfigFields,
@@ -858,12 +893,12 @@ export function setPiNotifySpawnForTests(spawnImpl: PiNotifySpawn | undefined):
858
893
 
859
894
  /**
860
895
  * @brief Dispatches prompt-end notify, sound, and Pushover effects for one agent-end payload.
861
- * @details Classifies the terminal outcome, executes `PI_NOTIFY_CMD` when command-notify prerequisites are satisfied, executes the configured sound command when sound prerequisites are satisfied, and dispatches the native Pushover request when Pushover prerequisites are satisfied. Runtime is O(m + c + b) in message count, command length, and Pushover payload size. Side effects include child-process spawning and outbound HTTPS requests.
896
+ * @details Classifies the terminal outcome, executes `PI_NOTIFY_CMD` when command-notify prerequisites are satisfied, executes the configured sound command when sound prerequisites are satisfied, and dispatches the native Pushover request when Pushover prerequisites are satisfied; `%%OUTPUT%%` resolves from the caller-supplied final agent output and Pushover bodies are head-truncated to the provider message limit. Runtime is O(m + c + b) in message count, command length, and Pushover payload size. Side effects include child-process spawning and outbound HTTPS requests.
862
897
  * @param[in] config {PiNotifyConfigFields} Effective notification configuration.
863
898
  * @param[in] event {Pick<AgentEndEvent, "messages">} Agent-end payload subset.
864
899
  * @param[in] request {PiNotifyEventRequest | undefined} Optional prompt-end request metadata used for command-notify and Pushover substitution.
865
900
  * @return {void} No return value.
866
- * @satisfies REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-199
901
+ * @satisfies REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-199, REQ-443, REQ-444, REQ-445
867
902
  */
868
903
  export function runPiNotifyEffects(
869
904
  config: PiNotifyConfigFields,
package/src/index.ts CHANGED
@@ -1498,16 +1498,50 @@ async function finalizeMatchedPromptSuccess(
1498
1498
  }
1499
1499
  }
1500
1500
 
1501
+ /**
1502
+ * @brief Extracts the final agent output text for prompt-end `%%OUTPUT%%` substitution.
1503
+ * @details Scans the `agent_end` payload messages backwards for the last assistant message and concatenates its `type === "text"` content blocks while skipping `thinking` and tool-call blocks, returning the empty string for non-agent payloads and for last assistant messages without text blocks so non-agentic command notifications never substitute stale output. Runtime is O(m + n) in message and text-block count. No external state is mutated.
1504
+ * @param[in] event {unknown} Raw `agent_end` lifecycle event payload.
1505
+ * @return {string} Concatenated final assistant text blocks, or the empty string when unavailable.
1506
+ * @satisfies REQ-443, REQ-444
1507
+ */
1508
+ function buildAgentEndOutputText(event: unknown): string {
1509
+ const messages = (event as { messages?: unknown } | undefined)?.messages;
1510
+ if (!Array.isArray(messages)) {
1511
+ return "";
1512
+ }
1513
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1514
+ const message = messages[index] as { role?: unknown; content?: unknown } | undefined;
1515
+ if (typeof message !== "object" || message === null || message.role !== "assistant") {
1516
+ continue;
1517
+ }
1518
+ const content = Array.isArray(message.content) ? message.content : [];
1519
+ let output = "";
1520
+ for (const block of content) {
1521
+ if (
1522
+ typeof block === "object"
1523
+ && block !== null
1524
+ && (block as { type?: unknown }).type === "text"
1525
+ && typeof (block as { text?: unknown }).text === "string"
1526
+ ) {
1527
+ output += (block as { text: string }).text;
1528
+ }
1529
+ }
1530
+ return output;
1531
+ }
1532
+ return "";
1533
+ }
1534
+
1501
1535
  /**
1502
1536
  * @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
1503
- * @details Applies session-start-specific resource validation, project-config refresh, idle-time shared-tokenizer and context-file measurement pre-warm, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. After the status update, captures the exact `before_provider_request` provider payload for the live active or pending prompt run by reloading the prompt debug gating configuration from the prompt run's original `base-path` (worktree-backed checkouts do not contain the untracked `.pi-usereq.json`, so the cwd-derived cached config would silently disable capture), flushes the process-scoped captured payload as one `<timestamp>-<req-command>-error-<status>` file whenever `after_provider_response` reports a status >= 400 or whenever one finalized `message_end` assistant error message carries a leading HTTP status >= 400 (the provider-independent fallback for provider APIs whose SDK clients throw before emitting `after_provider_response` for non-2xx statuses) without requiring live workflow state, and discards the capture at prompt-run end so stale payloads never reach later runs. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects, logs dedicated workflow-closure diagnostics, classifies the prompt outcome, and for every matched successful worktree-backed completion defers the restore switch, stash-assisted merge, and worktree deletion to `agent_settled` when the running pi host supports that 0.80.4+ event (because its `switchSession` awaits the active agent run to become idle and would deadlock inside `agent_end`), or executes the finalization directly at `agent_end` when the host does not emit `agent_settled`. On `agent_settled`, reuses persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the deferred stash-assisted merge-and-delete finalization path, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful closure handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
1537
+ * @details Applies session-start-specific resource validation, project-config refresh, idle-time shared-tokenizer and context-file measurement pre-warm, startup-tool enablement, and selected debug-tool logging before forwarding the originating hook name and payload into the shared `updateExtensionStatus(...)` pipeline. After the status update, captures the exact `before_provider_request` provider payload for the live active or pending prompt run by reloading the prompt debug gating configuration from the prompt run's original `base-path` (worktree-backed checkouts do not contain the untracked `.pi-usereq.json`, so the cwd-derived cached config would silently disable capture), flushes the process-scoped captured payload as one `<timestamp>-<req-command>-error-<status>` file whenever `after_provider_response` reports a status >= 400 or whenever one finalized `message_end` assistant error message carries a leading HTTP status >= 400 (the provider-independent fallback for provider APIs whose SDK clients throw before emitting `after_provider_response` for non-2xx statuses) without requiring live workflow state, and discards the capture at prompt-run end so stale payloads never reach later runs. Before `agent_start`, re-verifies any prepared prompt execution session switch. On `agent_end`, dispatches configured command-notify, sound, and prompt-specific Pushover effects with the final assistant output text feeding `%%OUTPUT%%` placeholder substitution, logs dedicated workflow-closure diagnostics, classifies the prompt outcome, and for every matched successful worktree-backed completion defers the restore switch, stash-assisted merge, and worktree deletion to `agent_settled` when the running pi host supports that 0.80.4+ event (because its `switchSession` awaits the active agent run to become idle and would deadlock inside `agent_end`), or executes the finalization directly at `agent_end` when the host does not emit `agent_settled`. On `agent_settled`, reuses persisted replacement-session command contexts when event contexts omit `switchSession()`, executes the deferred stash-assisted merge-and-delete finalization path, emits a warning-only notification when restored `base-path` changes are reapplied after merge, tolerates stale replacement-session notification contexts after session replacement, retains the worktree plus notifies closure failure for interrupted or failed outcomes, logs selected prompt workflow transitions, and transitions workflow state through `merging`, `error`, and `idle` as required. On `session_shutdown`, captures pre-update prompt snapshots so workflow-shutdown diagnostics and same-runtime command continuation preserve the active prompt workflow state across switch-triggered rebinding, then disposes the shared controller. Runtime is dominated by configuration loading during `session_start` and git finalization during matched successful closure handling; all other hooks are O(1). Side effects include resource checks, active-tool mutation, active-session replacement, status updates, live-ticker disposal on shutdown, optional child-process spawning, outbound HTTPS requests, branch merges, worktree deletion, and optional debug-log writes.
1504
1538
  * @param[in] pi {ExtensionAPI} Active extension API instance.
1505
1539
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
1506
1540
  * @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
1507
1541
  * @param[in] event {unknown} Hook payload forwarded by pi.
1508
1542
  * @param[in] ctx {ExtensionContext} Active extension context.
1509
1543
  * @return {Promise<void>} Promise resolved when hook processing completes.
1510
- * @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-291, REQ-292, REQ-421, REQ-422, REQ-425, REQ-440
1544
+ * @satisfies REQ-117, REQ-118, REQ-119, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-176, REQ-178, REQ-184, REQ-185, REQ-186, REQ-187, REQ-208, REQ-209, REQ-221, REQ-228, REQ-229, REQ-230, REQ-244, REQ-245, REQ-246, REQ-247, REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-291, REQ-292, REQ-421, REQ-422, REQ-425, REQ-440, REQ-443, REQ-444
1511
1545
  */
1512
1546
  async function handleExtensionStatusEvent(
1513
1547
  pi: ExtensionAPI,
@@ -1569,6 +1603,7 @@ async function handleExtensionStatusEvent(
1569
1603
  promptArgs: activePromptRequest.promptArgs,
1570
1604
  basePath: activePromptRequest.basePath,
1571
1605
  completionTimeMs: Math.max(0, Date.now() - statusController.state.runStartTimeMs),
1606
+ agentOutput: buildAgentEndOutputText(event),
1572
1607
  }
1573
1608
  : undefined;
1574
1609
  updateExtensionStatus(statusController, hookName, event, ctx);
@@ -2710,7 +2745,7 @@ async function configurePiNotifyEventMenu(
2710
2745
  * @details Serializes the global enable flag, shared-event submenu launcher, priority, title, text, and credential rows into right-valued menu items appended after the sound-command rows, dims and disables the enable row until both credentials are populated, renders the locked value as `configure user/token keys first`, and escapes control characters for the single-line `Pushover text` value. Runtime is O(n) in the rendered text-template length. No external state is mutated.
2711
2746
  * @param[in] config {UseReqConfig} Effective project configuration.
2712
2747
  * @return {PiUsereqSettingsMenuChoice[]} Ordered direct Pushover rows.
2713
- * @satisfies REQ-163, REQ-165, REQ-172, REQ-184, REQ-185, REQ-198, REQ-234, REQ-235
2748
+ * @satisfies REQ-163, REQ-165, REQ-172, REQ-184, REQ-185, REQ-198, REQ-234, REQ-235, REQ-443, REQ-444, REQ-446
2714
2749
  */
2715
2750
  function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
2716
2751
  const pushoverCredentialsReady = hasPiNotifyPushoverCredentials(config);
@@ -2747,7 +2782,7 @@ function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuCh
2747
2782
  id: "notify-pushover-text",
2748
2783
  label: "Pushover text",
2749
2784
  value: formatPiNotifyControlSequenceText(config["notify-pushover-text"]),
2750
- description: "Edit the text template used for outbound Pushover messages.",
2785
+ description: "Edit the text template used for outbound Pushover messages. Tokens: %%PROMT%% %%BASE%% %%TIME%% %%ARGS%% %%OUTPUT%% %%RESULT%%.",
2751
2786
  },
2752
2787
  {
2753
2788
  id: "notify-pushover-user-key",