pi-usereq 0.85.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.85.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.85.0",
3
+ "version": "0.87.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Ogekuri/PI-useReq.git"
@@ -0,0 +1,182 @@
1
+ /**
2
+ * @file
3
+ * @brief Persists durable non-agentic command execution reports as custom session entries.
4
+ * @details Defines the stable custom-entry type, machine-readable report payload, builders for `req-references` outcome lines, and the pi guarded helpers that append the report entry and register its transcript renderer. Custom entries are stored in the session file, excluded from LLM context, rendered live through `entry_appended`, and re-rendered after any session restore, so transient UI-notification collapsing or chat rebuilds never lose the executed-commands outcome report. Runtime is O(n) in report-line count. Side effects are limited to session-entry persistence and renderer registration through the pi extension API.
5
+ */
6
+
7
+ import type {
8
+ CustomEntry,
9
+ EntryRenderOptions,
10
+ EntryRenderer,
11
+ ExtensionAPI,
12
+ Theme,
13
+ } from "@earendil-works/pi-coding-agent";
14
+ import { Text } from "@earendil-works/pi-tui";
15
+ import type { Component } from "@earendil-works/pi-tui";
16
+ import { formatRuntimePathForDisplay } from "./path-context.js";
17
+ import type { ReqReferencesCommandExecutionResult } from "./req-references-command.js";
18
+
19
+ /**
20
+ * @brief Declares the stable custom-entry type used for durable command execution reports.
21
+ * @details Identifies `pi-usereq` report entries inside the persisted session file so interactive mode renders them through the registered entry renderer after reload, resume, fork, and session switches. Lookup complexity is O(1).
22
+ */
23
+ export const COMMAND_REPORT_ENTRY_TYPE = "pi-usereq.command-report";
24
+
25
+ /**
26
+ * @brief Describes one durable command execution report entry payload.
27
+ * @details Stores the originating slash-command name, the report severity, the terminal outcome, and the ordered per-operation outcome lines rendered in the transcript. The interface is compile-time only and introduces no runtime cost.
28
+ */
29
+ export interface CommandReportEntryData {
30
+ command: string;
31
+ level: "info" | "error";
32
+ outcome: "success" | "error";
33
+ lines: string[];
34
+ }
35
+
36
+ /**
37
+ * @brief Describes the loosely-typed pi runtime surface required for durable report entries.
38
+ * @details Narrows the extension API to the optional `registerEntryRenderer(...)` and `appendEntry(...)` methods consumed by the guarded report helpers so legacy hosts and offline harnesses without custom-entry support degrade silently instead of throwing. The interface is compile-time only and introduces no runtime cost.
39
+ */
40
+ interface CommandReportEntryRegistrar {
41
+ registerEntryRenderer?: (
42
+ customType: string,
43
+ renderer: EntryRenderer<CommandReportEntryData>,
44
+ ) => void;
45
+ appendEntry?: (customType: string, data?: CommandReportEntryData) => void;
46
+ }
47
+
48
+ /**
49
+ * @brief Validates one loosely-typed payload as a command report entry payload.
50
+ * @details Accepts only payloads with a non-empty command string, the documented `info|error` level, the documented `success|error` outcome, and a string-only lines array so malformed session data never reaches the transcript renderer. Runtime is O(n) in line count. No external state is mutated.
51
+ * @param[in] value {unknown} Candidate persisted payload.
52
+ * @return {boolean} `true` when the payload satisfies the report entry contract.
53
+ */
54
+ function isCommandReportEntryData(value: unknown): value is CommandReportEntryData {
55
+ if (typeof value !== "object" || value === null) {
56
+ return false;
57
+ }
58
+ const candidate = value as Partial<CommandReportEntryData>;
59
+ return typeof candidate.command === "string"
60
+ && candidate.command.trim() !== ""
61
+ && (candidate.level === "info" || candidate.level === "error")
62
+ && (candidate.outcome === "success" || candidate.outcome === "error")
63
+ && Array.isArray(candidate.lines)
64
+ && candidate.lines.every((line) => typeof line === "string");
65
+ }
66
+
67
+ /**
68
+ * @brief Builds the ordered per-operation outcome lines for one completed `req-references` run.
69
+ * @details Emits one machine-readable line per executed operation — terminal outcome, references-file regeneration result, guarded git-commit result, and post-run repository cleanliness — so the durable report states exactly which commands ran and with which outcome. Runtime is O(n) in path length. No external state is mutated.
70
+ * @param[in] executionResult {ReqReferencesCommandExecutionResult} Per-operation outcome facts of the completed run.
71
+ * @return {string[]} Ordered report lines starting with the terminal outcome.
72
+ * @satisfies REQ-436, REQ-437, REQ-438
73
+ */
74
+ export function buildReqReferencesCommandReportLines(
75
+ executionResult: ReqReferencesCommandExecutionResult,
76
+ ): string[] {
77
+ const referencesDisplayPath = formatRuntimePathForDisplay(executionResult.referencesPath);
78
+ return [
79
+ `outcome: success`,
80
+ `references file ${referencesDisplayPath}: ${
81
+ executionResult.referencesChanged ? "updated" : "already matched the generated content"
82
+ }`,
83
+ `git commit: ${
84
+ executionResult.commitCreated
85
+ ? `created "${executionResult.commitMessage}"`
86
+ : "skipped (references content matched HEAD)"
87
+ }`,
88
+ "repository: clean after run",
89
+ ];
90
+ }
91
+
92
+ /**
93
+ * @brief Builds the durable report entry payload for one completed `req-references` run.
94
+ * @details Pairs the invokable command name with the per-operation outcome lines and the `success` outcome. Runtime is O(n) in report-line count. No external state is mutated.
95
+ * @param[in] executionResult {ReqReferencesCommandExecutionResult} Per-operation outcome facts of the completed run.
96
+ * @return {CommandReportEntryData} Report entry payload for session persistence.
97
+ * @satisfies REQ-436, REQ-437, REQ-438
98
+ */
99
+ export function buildReqReferencesCommandReportEntryData(
100
+ executionResult: ReqReferencesCommandExecutionResult,
101
+ ): CommandReportEntryData {
102
+ return {
103
+ command: "req-references",
104
+ level: "info",
105
+ outcome: "success",
106
+ lines: buildReqReferencesCommandReportLines(executionResult),
107
+ };
108
+ }
109
+
110
+ /**
111
+ * @brief Builds the durable report entry payload for one failed non-agentic command run.
112
+ * @details Records the invokable command name, the `error` outcome, and the surfaced failure diagnostic so failed runs keep their outcome evidence in the transcript. Runtime is O(n) in message length. No external state is mutated.
113
+ * @param[in] command {string} Invokable slash-command name that failed.
114
+ * @param[in] errorMessage {string} Surfaced failure diagnostic.
115
+ * @return {CommandReportEntryData} Report entry payload for session persistence.
116
+ */
117
+ export function buildCommandReportErrorEntryData(
118
+ command: string,
119
+ errorMessage: string,
120
+ ): CommandReportEntryData {
121
+ return {
122
+ command,
123
+ level: "error",
124
+ outcome: "error",
125
+ lines: [`outcome: error`, `error: ${errorMessage}`],
126
+ };
127
+ }
128
+
129
+ /**
130
+ * @brief Appends one durable command report entry to the active session when the host supports custom entries.
131
+ * @details Probes the optional `pi.appendEntry(...)` surface before use so legacy hosts and offline harnesses without custom-entry support skip persistence silently; supported hosts persist the payload as a `custom` session entry that survives session restore while remaining excluded from LLM context. Runtime is O(1) plus delegated session-entry persistence. Side effects include session-file entry persistence on supported hosts.
132
+ * @param[in] pi {ExtensionAPI} Active extension API instance.
133
+ * @param[in] data {CommandReportEntryData} Report entry payload to persist.
134
+ * @return {boolean} `true` when the entry was appended; `false` when the host lacks the custom-entry surface.
135
+ */
136
+ export function appendCommandReportEntry(pi: ExtensionAPI, data: CommandReportEntryData): boolean {
137
+ const registrar = pi as ExtensionAPI & CommandReportEntryRegistrar;
138
+ if (typeof registrar.appendEntry !== "function") {
139
+ return false;
140
+ }
141
+ registrar.appendEntry(COMMAND_REPORT_ENTRY_TYPE, data);
142
+ return true;
143
+ }
144
+
145
+ /**
146
+ * @brief Renders one durable command report entry as a transcript component.
147
+ * @details Maps the report payload into a themed multi-line `Text` — header colored `error` for failed runs and `accent` otherwise, body lines colored `error` for failed runs and `muted` otherwise — and returns `undefined` for malformed payloads so the host transcript skips undisplayable entries. Runtime is O(n) in aggregate report-line length. No external state is mutated.
148
+ * @param[in] entry {CustomEntry<CommandReportEntryData>} Persisted custom report entry.
149
+ * @param[in] _options {EntryRenderOptions} Host render options; retained for renderer-signature compatibility.
150
+ * @param[in] theme {Theme} Active pi theme used for color tokens.
151
+ * @return {Component | undefined} Transcript component, or `undefined` when the payload is malformed.
152
+ */
153
+ function renderCommandReportEntry(
154
+ entry: CustomEntry<CommandReportEntryData>,
155
+ _options: EntryRenderOptions,
156
+ theme: Theme,
157
+ ): Component | undefined {
158
+ if (!isCommandReportEntryData(entry.data)) {
159
+ return undefined;
160
+ }
161
+ const data = entry.data;
162
+ const isError = data.level === "error";
163
+ const headerColor = isError ? "error" : "accent";
164
+ const bodyColor = isError ? "error" : "muted";
165
+ const header = theme.fg(headerColor, `pi-usereq ${data.command}: execution report`);
166
+ const body = data.lines.map((line) => theme.fg(bodyColor, line)).join("\n");
167
+ return new Text(`${header}\n${body}`, 1, 0);
168
+ }
169
+
170
+ /**
171
+ * @brief Registers the durable command report transcript renderer when the host supports entry rendering.
172
+ * @details Probes the optional `pi.registerEntryRenderer(...)` surface before use so legacy hosts and offline harnesses without custom-entry rendering skip registration silently; supported hosts bind the renderer for `pi-usereq.command-report` entries so appended reports render in the live transcript and re-render after every session restore. Runtime is O(1). Side effects include entry-renderer registration on supported hosts.
173
+ * @param[in] pi {ExtensionAPI} Active extension API instance.
174
+ * @return {void} No return value.
175
+ */
176
+ export function registerCommandReportEntryRenderer(pi: ExtensionAPI): void {
177
+ const registrar = pi as ExtensionAPI & CommandReportEntryRegistrar;
178
+ if (typeof registrar.registerEntryRenderer !== "function") {
179
+ return;
180
+ }
181
+ registrar.registerEntryRenderer(COMMAND_REPORT_ENTRY_TYPE, renderCommandReportEntry);
182
+ }
@@ -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
@@ -174,6 +174,12 @@ import {
174
174
  shouldReplayDeferredNotificationsOnSessionStart,
175
175
  } from "./core/notification-replay.js";
176
176
  import { ensureBundledResourcesAccessible, readBundledPromptDescription } from "./core/resources.js";
177
+ import {
178
+ appendCommandReportEntry,
179
+ buildCommandReportErrorEntryData,
180
+ buildReqReferencesCommandReportEntryData,
181
+ registerCommandReportEntryRenderer,
182
+ } from "./core/command-report-entry.js";
177
183
  import { ReqError } from "./core/errors.js";
178
184
  import {
179
185
  PI_USEREQ_STATUS_HOOK_NAMES,
@@ -1492,16 +1498,50 @@ async function finalizeMatchedPromptSuccess(
1492
1498
  }
1493
1499
  }
1494
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
+
1495
1535
  /**
1496
1536
  * @brief Handles one intercepted pi lifecycle hook for pi-usereq status updates.
1497
- * @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.
1498
1538
  * @param[in] pi {ExtensionAPI} Active extension API instance.
1499
1539
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
1500
1540
  * @param[in] hookName {PiUsereqStatusHookName} Intercepted hook name.
1501
1541
  * @param[in] event {unknown} Hook payload forwarded by pi.
1502
1542
  * @param[in] ctx {ExtensionContext} Active extension context.
1503
1543
  * @return {Promise<void>} Promise resolved when hook processing completes.
1504
- * @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
1505
1545
  */
1506
1546
  async function handleExtensionStatusEvent(
1507
1547
  pi: ExtensionAPI,
@@ -1563,6 +1603,7 @@ async function handleExtensionStatusEvent(
1563
1603
  promptArgs: activePromptRequest.promptArgs,
1564
1604
  basePath: activePromptRequest.basePath,
1565
1605
  completionTimeMs: Math.max(0, Date.now() - statusController.state.runStartTimeMs),
1606
+ agentOutput: buildAgentEndOutputText(event),
1566
1607
  }
1567
1608
  : undefined;
1568
1609
  updateExtensionStatus(statusController, hookName, event, ctx);
@@ -2704,7 +2745,7 @@ async function configurePiNotifyEventMenu(
2704
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.
2705
2746
  * @param[in] config {UseReqConfig} Effective project configuration.
2706
2747
  * @return {PiUsereqSettingsMenuChoice[]} Ordered direct Pushover rows.
2707
- * @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
2708
2749
  */
2709
2750
  function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuChoice[] {
2710
2751
  const pushoverCredentialsReady = hasPiNotifyPushoverCredentials(config);
@@ -2741,7 +2782,7 @@ function buildPiNotifyPushoverRows(config: UseReqConfig): PiUsereqSettingsMenuCh
2741
2782
  id: "notify-pushover-text",
2742
2783
  label: "Pushover text",
2743
2784
  value: formatPiNotifyControlSequenceText(config["notify-pushover-text"]),
2744
- 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%%.",
2745
2786
  },
2746
2787
  {
2747
2788
  id: "notify-pushover-user-key",
@@ -3493,11 +3534,11 @@ function registerReqResetCommand(
3493
3534
 
3494
3535
  /**
3495
3536
  * @brief Registers the specialized `req-references` slash command.
3496
- * @details Registers the non-agentic references-maintenance command that rejects non-`idle` invocations by transitioning workflow state to `error` before direct execution, otherwise reuses slash-command-owned git validation, transitions workflow state through `checking|running|idle`, regenerates `REFERENCES.md` directly from configured source directories, stages only the generated file, creates the fixed-message git commit, verifies repository cleanliness, and emits execution notifications that conform to the operations actually performed — one stating whether the references file was updated or already matched the generated content and one stating whether the fixed-message commit was created or skipped — regardless of the update and commit outcomes, without starting an LLM session or creating a worktree. Runtime is dominated by git subprocess execution plus source-summary generation. Side effects include command registration, status-controller mutation, filesystem writes, git index/history mutation, and user notifications.
3537
+ * @details Registers the non-agentic references-maintenance command that rejects non-`idle` invocations by transitioning workflow state to `error` before direct execution, otherwise reuses slash-command-owned git validation, transitions workflow state through `checking|running|idle`, regenerates `REFERENCES.md` directly from configured source directories, stages only the generated file, creates the fixed-message git commit, verifies repository cleanliness, appends one durable `pi-usereq.command-report` session entry recording the executed operations with their outcomes so the report renders in the transcript and survives session restore, and emits execution notifications that conform to the operations actually performed — one stating whether the references file was updated or already matched the generated content and one stating whether the fixed-message commit was created or skipped — regardless of the update and commit outcomes, without starting an LLM session or creating a worktree. Runtime is dominated by git subprocess execution plus source-summary generation. Side effects include command registration, status-controller mutation, filesystem writes, git index/history mutation, session-entry persistence, and user notifications.
3497
3538
  * @param[in] pi {ExtensionAPI} Active extension API instance.
3498
3539
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
3499
3540
  * @return {void} No return value.
3500
- * @satisfies REQ-200, REQ-221, REQ-224, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303, REQ-437, REQ-438, REQ-441
3541
+ * @satisfies REQ-200, REQ-221, REQ-224, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303, REQ-436, REQ-437, REQ-438, REQ-441
3501
3542
  */
3502
3543
  function registerReqReferencesCommand(
3503
3544
  pi: ExtensionAPI,
@@ -3526,6 +3567,7 @@ function registerReqReferencesCommand(
3526
3567
  const executionResult = executeReqReferencesCommandExecution(executionPlan, config);
3527
3568
  setPiUsereqWorkflowState(statusController, "idle", ctx);
3528
3569
  const referencesDisplayPath = formatRuntimePathForDisplay(executionResult.referencesPath);
3570
+ appendCommandReportEntry(pi, buildReqReferencesCommandReportEntryData(executionResult));
3529
3571
  notifyContextSafely(
3530
3572
  ctx,
3531
3573
  executionResult.referencesChanged
@@ -3553,6 +3595,7 @@ function registerReqReferencesCommand(
3553
3595
  statusController.state.activePromptRequest = undefined;
3554
3596
  setPiUsereqWorkflowState(statusController, "error", ctx);
3555
3597
  const message = error instanceof Error ? error.message : String(error);
3598
+ appendCommandReportEntry(pi, buildCommandReportErrorEntryData("req-references", message));
3556
3599
  notifyContextSafely(ctx, message, "error");
3557
3600
  throw error;
3558
3601
  }
@@ -5361,13 +5404,14 @@ function registerConfigCommands(
5361
5404
 
5362
5405
  /**
5363
5406
  * @brief Registers the complete pi-usereq extension.
5364
- * @details Persists the live extension api into process-scoped storage on every bind so model re-application after session replacement binds to the api of the currently active session runner instead of an invalidated captured api, then validates installation-owned bundled resources, schedules a best-effort idle-time shared-tokenizer plus canonical-context-file measurement pre-warm so the first configuration-menu render never pays the one-time `js-tiktoken` module load or first content encodes synchronously, registers the specialized `req-reset` and `req-references` commands plus bundled prompt-backed commands and agent tools, conditionally registers config-gated debug tool wrapper commands when the current project enables them, registers configuration commands, registers the configurable notification-sound shortcut when the runtime supports shortcuts, and installs shared wrappers for all supported pi lifecycle hooks so status telemetry, context usage, prompt timing, cumulative runtime, prompt-specific Pushover metadata, tool-result debug logging, and prompt-orchestration effects remain synchronized with runtime events. Runtime is O(h) in hook count during registration. Side effects include filesystem reads, command/tool/shortcut registration, UI updates, active-tool changes, process-scoped extension-api persistence, optional debug-log writes, and timer scheduling.
5407
+ * @details Persists the live extension api into process-scoped storage on every bind so model re-application after session replacement binds to the api of the currently active session runner instead of an invalidated captured api, registers the durable `pi-usereq.command-report` transcript entry renderer, then validates installation-owned bundled resources, schedules a best-effort idle-time shared-tokenizer plus canonical-context-file measurement pre-warm so the first configuration-menu render never pays the one-time `js-tiktoken` module load or first content encodes synchronously, registers the specialized `req-reset` and `req-references` commands plus bundled prompt-backed commands and agent tools, conditionally registers config-gated debug tool wrapper commands when the current project enables them, registers configuration commands, registers the configurable notification-sound shortcut when the runtime supports shortcuts, and installs shared wrappers for all supported pi lifecycle hooks so status telemetry, context usage, prompt timing, cumulative runtime, prompt-specific Pushover metadata, tool-result debug logging, and prompt-orchestration effects remain synchronized with runtime events. Runtime is O(h) in hook count during registration. Side effects include filesystem reads, command/tool/shortcut registration, UI updates, active-tool changes, process-scoped extension-api persistence, optional debug-log writes, and timer scheduling.
5365
5408
  * @param[in] pi {ExtensionAPI} Active extension API instance.
5366
5409
  * @return {void} No return value.
5367
5410
  * @satisfies DES-002, DES-015, REQ-004, REQ-005, REQ-009, REQ-044, REQ-067, REQ-068, REQ-109, REQ-111, REQ-112, REQ-113, REQ-114, REQ-115, REQ-116, REQ-117, REQ-118, REQ-119, REQ-120, REQ-121, REQ-122, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-131, REQ-132, REQ-133, REQ-134, REQ-137, REQ-159, REQ-163, REQ-164, REQ-165, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172, REQ-174, REQ-179, REQ-180, REQ-184, REQ-188, REQ-190, REQ-191, REQ-192, REQ-193, REQ-194, REQ-195, REQ-196, REQ-197, REQ-236, REQ-237, REQ-238, REQ-239, REQ-240, REQ-241, REQ-242, REQ-243, REQ-244, REQ-245, REQ-246, REQ-247, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303, REQ-304, REQ-305, REQ-306, REQ-312, REQ-313, REQ-323, REQ-324, REQ-325, REQ-326, REQ-327
5368
5411
  */
5369
5412
  export default function piUsereqExtension(pi: ExtensionAPI): void {
5370
5413
  writePersistedPromptCommandRuntimeApi(pi as unknown as PersistedPromptCommandRuntimeApi);
5414
+ registerCommandReportEntryRenderer(pi);
5371
5415
  const statusController = createPiUsereqStatusController();
5372
5416
  ensureBundledResourcesAccessible();
5373
5417
  try {