pi-usereq 0.85.0 → 0.86.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.86.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+">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-usereq",
3
- "version": "0.85.0",
3
+ "version": "0.86.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
+ }
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,
@@ -3493,11 +3499,11 @@ function registerReqResetCommand(
3493
3499
 
3494
3500
  /**
3495
3501
  * @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.
3502
+ * @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
3503
  * @param[in] pi {ExtensionAPI} Active extension API instance.
3498
3504
  * @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
3499
3505
  * @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
3506
+ * @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
3507
  */
3502
3508
  function registerReqReferencesCommand(
3503
3509
  pi: ExtensionAPI,
@@ -3526,6 +3532,7 @@ function registerReqReferencesCommand(
3526
3532
  const executionResult = executeReqReferencesCommandExecution(executionPlan, config);
3527
3533
  setPiUsereqWorkflowState(statusController, "idle", ctx);
3528
3534
  const referencesDisplayPath = formatRuntimePathForDisplay(executionResult.referencesPath);
3535
+ appendCommandReportEntry(pi, buildReqReferencesCommandReportEntryData(executionResult));
3529
3536
  notifyContextSafely(
3530
3537
  ctx,
3531
3538
  executionResult.referencesChanged
@@ -3553,6 +3560,7 @@ function registerReqReferencesCommand(
3553
3560
  statusController.state.activePromptRequest = undefined;
3554
3561
  setPiUsereqWorkflowState(statusController, "error", ctx);
3555
3562
  const message = error instanceof Error ? error.message : String(error);
3563
+ appendCommandReportEntry(pi, buildCommandReportErrorEntryData("req-references", message));
3556
3564
  notifyContextSafely(ctx, message, "error");
3557
3565
  throw error;
3558
3566
  }
@@ -5361,13 +5369,14 @@ function registerConfigCommands(
5361
5369
 
5362
5370
  /**
5363
5371
  * @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.
5372
+ * @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
5373
  * @param[in] pi {ExtensionAPI} Active extension API instance.
5366
5374
  * @return {void} No return value.
5367
5375
  * @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
5376
  */
5369
5377
  export default function piUsereqExtension(pi: ExtensionAPI): void {
5370
5378
  writePersistedPromptCommandRuntimeApi(pi as unknown as PersistedPromptCommandRuntimeApi);
5379
+ registerCommandReportEntryRenderer(pi);
5371
5380
  const statusController = createPiUsereqStatusController();
5372
5381
  ensureBundledResourcesAccessible();
5373
5382
  try {