pi-usereq 0.4.0 → 0.5.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/.gitignore +3 -0
- package/CHANGELOG.md +31 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/{req → pi-usereq}/docs/REFERENCES.md +196 -159
- package/{req → pi-usereq}/docs/REQUIREMENTS.md +65 -44
- package/{req → pi-usereq}/docs/WORKFLOW.md +66 -73
- package/src/core/config.ts +39 -12
- package/src/core/extension-status.ts +94 -58
- package/src/core/pi-notify.ts +198 -11
- package/src/core/runtime-project-paths.ts +3 -32
- package/src/core/settings-menu.ts +9 -4
- package/src/core/static-check.ts +35 -171
- package/src/index.ts +281 -86
- package/tests/attended-results-scenarios.ts +3 -13
- package/tests/cli-command-option-parity.test.ts +21 -12
- package/tests/debug-extension-harness.test.ts +11 -7
- package/tests/extension-registration.test.ts +371 -71
- package/tests/oracle-project.test.ts +8 -3
- package/tests/oracle-standalone.test.ts +7 -13
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_c.c.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_cpp.cpp.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_csharp.cs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_elixir.ex.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_go.go.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_haskell.hs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_java.java.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_javascript.js.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_kotlin.kt.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_lua.lua.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_perl.pl.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_php.php.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_python.py.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_ruby.rb.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_rust.rs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_scala.scala.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_shell.sh.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_swift.swift.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_typescript.ts.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-pylance/fixture_zig.zig.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_c.c.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_cpp.cpp.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_csharp.cs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_elixir.ex.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_go.go.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_haskell.hs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_java.java.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_javascript.js.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_kotlin.kt.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_lua.lua.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_perl.pl.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_php.php.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_python.py.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_ruby.rb.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_rust.rs.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_scala.scala.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_shell.sh.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_swift.swift.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_typescript.ts.json +0 -5
- package/tests/fixtures_attended_results/standalone/test-static-check-ruff/fixture_zig.zig.json +0 -5
package/src/core/pi-notify.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file
|
|
3
|
-
* @brief Implements pi-usereq terminal-beep
|
|
4
|
-
* @details Centralizes configuration defaults, status serialization, agent-end outcome classification, terminal notification dispatch,
|
|
3
|
+
* @brief Implements pi-usereq terminal-beep, external sound-hook, and Pushover notification helpers.
|
|
4
|
+
* @details Centralizes configuration defaults, status serialization, agent-end outcome classification, terminal notification dispatch, successful-run external sound-command execution, and successful-run Pushover delivery. Runtime is O(m + c + b) in `agent_end` message count plus command length and Pushover payload size. Side effects include stdout writes, detached child-process spawning, and outbound HTTPS requests.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { execFile, spawn } from "node:child_process";
|
|
8
|
+
import * as https from "node:https";
|
|
8
9
|
import type { AgentEndEvent } from "@mariozechner/pi-coding-agent";
|
|
9
10
|
import { getInstallationPath } from "./path-context.js";
|
|
10
11
|
import type { UseReqConfig } from "./config.js";
|
|
@@ -61,9 +62,32 @@ export const DEFAULT_PI_NOTIFY_SOUND_MID_CMD = "paplay --volume=43690 %%INSTALLA
|
|
|
61
62
|
*/
|
|
62
63
|
export const DEFAULT_PI_NOTIFY_SOUND_HIGH_CMD = "paplay --volume=65535 %%INSTALLATION_PATH%%/resources/sounds/Soft-high-tech-notification-sound-effect.mp3";
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @brief Enumerates supported Pushover priorities.
|
|
67
|
+
* @details Restricts persisted Pushover delivery to the canonical normal and high-priority values accepted by the user-facing configuration menu. Access complexity is O(1).
|
|
68
|
+
*/
|
|
69
|
+
export const PI_NOTIFY_PUSHOVER_PRIORITIES = [0, 1] as const;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @brief Represents one supported Pushover priority value.
|
|
73
|
+
* @details Narrows Pushover configuration parsing and request serialization to the canonical `0|1` priority domain. Compile-time only and introduces no runtime cost.
|
|
74
|
+
*/
|
|
75
|
+
export type PiNotifyPushoverPriority = (typeof PI_NOTIFY_PUSHOVER_PRIORITIES)[number];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @brief Describes one successful prompt-completion payload routed to Pushover.
|
|
79
|
+
* @details Stores the prompt command name, substituted prompt arguments, runtime base path, and successful completion duration used to build one Pushover API request. The interface is compile-time only and introduces no runtime side effects.
|
|
80
|
+
*/
|
|
81
|
+
export interface PiNotifyPushoverRequest {
|
|
82
|
+
promptName: string;
|
|
83
|
+
promptArgs: string;
|
|
84
|
+
basePath: string;
|
|
85
|
+
completionTimeMs: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
64
88
|
/**
|
|
65
89
|
* @brief Describes the configuration fields consumed by pi-notify helpers.
|
|
66
|
-
* @details Narrows the full project config to the persisted notification
|
|
90
|
+
* @details Narrows the full project config to the persisted notification, sound-hook, and Pushover fields used by status rendering, prompt-end routing, and shortcut toggles. Compile-time only and introduces no runtime cost.
|
|
67
91
|
*/
|
|
68
92
|
export type PiNotifyConfigFields = Pick<
|
|
69
93
|
UseReqConfig,
|
|
@@ -72,11 +96,22 @@ export type PiNotifyConfigFields = Pick<
|
|
|
72
96
|
| "notify-beep-on-error"
|
|
73
97
|
| "notify-sound"
|
|
74
98
|
| "notify-sound-toggle-shortcut"
|
|
99
|
+
| "notify-pushover-global-disable"
|
|
100
|
+
| "notify-pushover-on-success"
|
|
101
|
+
| "notify-pushover-user-key"
|
|
102
|
+
| "notify-pushover-api-token"
|
|
103
|
+
| "notify-pushover-priority"
|
|
75
104
|
| "PI_NOTIFY_SOUND_LOW_CMD"
|
|
76
105
|
| "PI_NOTIFY_SOUND_MID_CMD"
|
|
77
106
|
| "PI_NOTIFY_SOUND_HIGH_CMD"
|
|
78
107
|
>;
|
|
79
108
|
|
|
109
|
+
/**
|
|
110
|
+
* @brief Stores the currently configured native HTTPS request function used for Pushover delivery.
|
|
111
|
+
* @details Defaults to `node:https.request` and can be replaced by deterministic tests so Pushover dispatch remains observable without real network I/O. Access complexity is O(1). Side effect: mutated only through the dedicated test hook.
|
|
112
|
+
*/
|
|
113
|
+
let piNotifyHttpsRequest: typeof https.request = https.request;
|
|
114
|
+
|
|
80
115
|
/**
|
|
81
116
|
* @brief Normalizes one persisted sound level.
|
|
82
117
|
* @details Accepts only canonical `none|low|mid|high` values and falls back to `none` for missing or invalid payloads. Runtime is O(1). No external state is mutated.
|
|
@@ -115,6 +150,28 @@ export function normalizePiNotifyCommand(value: unknown, fallback: string): stri
|
|
|
115
150
|
return typeof value === "string" && value.trim() ? value.trim() : fallback;
|
|
116
151
|
}
|
|
117
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @brief Normalizes one persisted Pushover credential string.
|
|
155
|
+
* @details Accepts any trimmed string so the project config can store raw Pushover user and token values verbatim and falls back to the empty string for missing or invalid payloads. Runtime is O(n) in credential length. No external state is mutated.
|
|
156
|
+
* @param[in] value {unknown} Raw persisted credential payload.
|
|
157
|
+
* @return {string} Canonical credential string.
|
|
158
|
+
* @satisfies REQ-163
|
|
159
|
+
*/
|
|
160
|
+
export function normalizePiNotifyPushoverCredential(value: unknown): string {
|
|
161
|
+
return typeof value === "string" ? value.trim() : "";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @brief Normalizes one persisted Pushover priority value.
|
|
166
|
+
* @details Accepts only canonical `0` and `1` values, treating numeric-string `"1"` as high priority and every other payload as normal priority. Runtime is O(1). No external state is mutated.
|
|
167
|
+
* @param[in] value {unknown} Raw persisted priority payload.
|
|
168
|
+
* @return {PiNotifyPushoverPriority} Canonical priority value.
|
|
169
|
+
* @satisfies REQ-163
|
|
170
|
+
*/
|
|
171
|
+
export function normalizePiNotifyPushoverPriority(value: unknown): PiNotifyPushoverPriority {
|
|
172
|
+
return value === 1 || value === "1" ? 1 : 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
118
175
|
/**
|
|
119
176
|
* @brief Formats enabled terminal-beep flags for status rendering.
|
|
120
177
|
* @details Emits the canonical comma-ordered enabled outcome tokens `end`, `esc`, and `err`, or `none` when all prompt-end beep flags are disabled. Runtime is O(1). No external state is mutated.
|
|
@@ -136,6 +193,17 @@ export function formatPiNotifyBeepStatus(config: PiNotifyConfigFields): string {
|
|
|
136
193
|
return enabledOutcomes.length > 0 ? enabledOutcomes.join(",") : "none";
|
|
137
194
|
}
|
|
138
195
|
|
|
196
|
+
/**
|
|
197
|
+
* @brief Formats the Pushover enable flag for status rendering.
|
|
198
|
+
* @details Serializes only the persisted successful-prompt enable setting so the status bar reports the dedicated Pushover toggle independently from any global-disable override. Runtime is O(1). No external state is mutated.
|
|
199
|
+
* @param[in] config {Pick<UseReqConfig, "notify-pushover-on-success">} Effective Pushover configuration subset.
|
|
200
|
+
* @return {string} `on` when successful-prompt Pushover delivery is enabled; otherwise `off`.
|
|
201
|
+
* @satisfies REQ-171
|
|
202
|
+
*/
|
|
203
|
+
export function formatPiNotifyPushoverStatus(config: Pick<UseReqConfig, "notify-pushover-on-success">): string {
|
|
204
|
+
return config["notify-pushover-on-success"] ? "on" : "off";
|
|
205
|
+
}
|
|
206
|
+
|
|
139
207
|
/**
|
|
140
208
|
* @brief Cycles one sound level through the canonical shortcut order.
|
|
141
209
|
* @details Advances persisted sound state in the exact order `none -> low -> mid -> high -> none`, enabling deterministic shortcut toggling and menu reuse. Runtime is O(1). No external state is mutated.
|
|
@@ -324,6 +392,120 @@ function buildPiNotifyBody(outcome: PiNotifyOutcome): string {
|
|
|
324
392
|
}
|
|
325
393
|
}
|
|
326
394
|
|
|
395
|
+
/**
|
|
396
|
+
* @brief Formats one successful prompt duration for Pushover titles.
|
|
397
|
+
* @details Floors the supplied completion duration to whole seconds, keeps minutes unbounded above 59, and zero-pads seconds to two digits so Pushover titles align with status-bar elapsed formatting. Runtime is O(1). No external state is mutated.
|
|
398
|
+
* @param[in] durationMs {number} Successful prompt duration in milliseconds.
|
|
399
|
+
* @return {string} Duration rendered as `M:SS`.
|
|
400
|
+
* @satisfies REQ-169
|
|
401
|
+
*/
|
|
402
|
+
function formatPiNotifyPushoverDuration(durationMs: number): string {
|
|
403
|
+
const totalSeconds = Math.max(0, Math.floor(durationMs / 1000));
|
|
404
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
405
|
+
const seconds = totalSeconds % 60;
|
|
406
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @brief Builds the Pushover notification title for one successful prompt.
|
|
411
|
+
* @details Serializes the prompt name, absolute runtime base path, and successful completion duration into the canonical `<prompt> @ <base-path> [<time>]` title required by the repository feature contract. Runtime is O(n) in path length. No external state is mutated.
|
|
412
|
+
* @param[in] request {PiNotifyPushoverRequest} Successful prompt metadata.
|
|
413
|
+
* @return {string} Pushover title string.
|
|
414
|
+
* @satisfies REQ-169
|
|
415
|
+
*/
|
|
416
|
+
function buildPiNotifyPushoverTitle(request: PiNotifyPushoverRequest): string {
|
|
417
|
+
return `${request.promptName} @ ${request.basePath} [${formatPiNotifyPushoverDuration(request.completionTimeMs)}]`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* @brief Builds the Pushover message body for one successful prompt.
|
|
422
|
+
* @details Reuses the raw prompt argument string substituted into `%%ARGS%%` so the pushed body remains traceable to the executed prompt invocation. Runtime is O(1). No external state is mutated.
|
|
423
|
+
* @param[in] request {PiNotifyPushoverRequest} Successful prompt metadata.
|
|
424
|
+
* @return {string} Pushover message body.
|
|
425
|
+
* @satisfies REQ-169
|
|
426
|
+
*/
|
|
427
|
+
function buildPiNotifyPushoverBody(request: PiNotifyPushoverRequest): string {
|
|
428
|
+
return request.promptArgs;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @brief Determines whether one successful prompt should trigger Pushover delivery.
|
|
433
|
+
* @details Requires a captured prompt request, the successful-prompt enable flag, the Pushover global-disable flag to remain false, and non-empty user plus token credentials. Runtime is O(1). No external state is mutated.
|
|
434
|
+
* @param[in] config {PiNotifyConfigFields} Effective notification configuration.
|
|
435
|
+
* @param[in] request {PiNotifyPushoverRequest | undefined} Successful prompt metadata.
|
|
436
|
+
* @return {boolean} `true` when Pushover delivery prerequisites are satisfied.
|
|
437
|
+
* @satisfies REQ-166, REQ-168, REQ-172
|
|
438
|
+
*/
|
|
439
|
+
function shouldRunPiNotifyPushover(
|
|
440
|
+
config: PiNotifyConfigFields,
|
|
441
|
+
request: PiNotifyPushoverRequest | undefined,
|
|
442
|
+
): boolean {
|
|
443
|
+
return request !== undefined
|
|
444
|
+
&& config["notify-pushover-on-success"]
|
|
445
|
+
&& !config["notify-pushover-global-disable"]
|
|
446
|
+
&& config["notify-pushover-user-key"] !== ""
|
|
447
|
+
&& config["notify-pushover-api-token"] !== "";
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* @brief Builds the Pushover API payload for one successful prompt.
|
|
452
|
+
* @details Encodes the configured token, user key, canonical title, priority, and substituted prompt-argument body as `application/x-www-form-urlencoded` fields accepted by the Pushover Message API. Runtime is O(n) in payload size. No external state is mutated.
|
|
453
|
+
* @param[in] config {PiNotifyConfigFields} Effective notification configuration.
|
|
454
|
+
* @param[in] request {PiNotifyPushoverRequest} Successful prompt metadata.
|
|
455
|
+
* @return {URLSearchParams} Encoded Pushover request payload.
|
|
456
|
+
* @satisfies REQ-167, REQ-169
|
|
457
|
+
*/
|
|
458
|
+
function buildPiNotifyPushoverPayload(
|
|
459
|
+
config: PiNotifyConfigFields,
|
|
460
|
+
request: PiNotifyPushoverRequest,
|
|
461
|
+
): URLSearchParams {
|
|
462
|
+
return new URLSearchParams({
|
|
463
|
+
token: config["notify-pushover-api-token"],
|
|
464
|
+
user: config["notify-pushover-user-key"],
|
|
465
|
+
title: buildPiNotifyPushoverTitle(request),
|
|
466
|
+
priority: String(config["notify-pushover-priority"]),
|
|
467
|
+
message: buildPiNotifyPushoverBody(request),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* @brief Dispatches one native HTTPS request to the Pushover Message API.
|
|
473
|
+
* @details Serializes the request body as URL-encoded form data, posts it to `https://api.pushover.net/1/messages.json`, drains the response, and ignores transport failures so prompt-end handling remains non-blocking. Runtime is dominated by outbound I/O. Side effects include one HTTPS request.
|
|
474
|
+
* @param[in] config {PiNotifyConfigFields} Effective notification configuration.
|
|
475
|
+
* @param[in] request {PiNotifyPushoverRequest} Successful prompt metadata.
|
|
476
|
+
* @return {void} No return value.
|
|
477
|
+
* @satisfies REQ-167, REQ-169
|
|
478
|
+
*/
|
|
479
|
+
function runPiNotifyPushoverRequest(
|
|
480
|
+
config: PiNotifyConfigFields,
|
|
481
|
+
request: PiNotifyPushoverRequest,
|
|
482
|
+
): void {
|
|
483
|
+
const url = new URL("https://api.pushover.net/1/messages.json");
|
|
484
|
+
const body = buildPiNotifyPushoverPayload(config, request).toString();
|
|
485
|
+
const httpRequest = piNotifyHttpsRequest(url, {
|
|
486
|
+
method: "POST",
|
|
487
|
+
headers: {
|
|
488
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
489
|
+
"content-length": String(Buffer.byteLength(body)),
|
|
490
|
+
},
|
|
491
|
+
}, (response) => {
|
|
492
|
+
response.on?.("error", () => undefined);
|
|
493
|
+
response.resume?.();
|
|
494
|
+
});
|
|
495
|
+
httpRequest.on("error", () => undefined);
|
|
496
|
+
httpRequest.end(body);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* @brief Replaces the native HTTPS request function used for Pushover delivery in deterministic tests.
|
|
501
|
+
* @details Accepts a drop-in `node:https.request` replacement and restores the native implementation when `undefined` is supplied. Runtime is O(1). Side effect: mutates the module-local Pushover transport hook.
|
|
502
|
+
* @param[in] requestImpl {typeof https.request | undefined} Replacement HTTPS request function.
|
|
503
|
+
* @return {void} No return value.
|
|
504
|
+
*/
|
|
505
|
+
export function setPiNotifyHttpsRequestForTests(requestImpl: typeof https.request | undefined): void {
|
|
506
|
+
piNotifyHttpsRequest = requestImpl ?? https.request;
|
|
507
|
+
}
|
|
508
|
+
|
|
327
509
|
/**
|
|
328
510
|
* @brief Quotes one installation path for shell substitution.
|
|
329
511
|
* @details Emits POSIX single-quoted literals for `sh -lc` execution and CMD double-quoted literals for `cmd.exe /c` execution so `%%INSTALLATION_PATH%%` substitutions preserve whitespace safely. Runtime is O(n) in path length. No external state is mutated.
|
|
@@ -397,16 +579,18 @@ export function runPiNotifySoundCommand(
|
|
|
397
579
|
}
|
|
398
580
|
|
|
399
581
|
/**
|
|
400
|
-
* @brief Dispatches prompt-end beep and
|
|
401
|
-
* @details Classifies the terminal outcome, emits the configured terminal notification only for the enabled outcome flag,
|
|
582
|
+
* @brief Dispatches prompt-end beep, sound, and optional Pushover effects for one agent-end payload.
|
|
583
|
+
* @details Classifies the terminal outcome, emits the configured terminal notification only for the enabled outcome flag, executes the configured external sound command only for successful completion with a non-disabled sound level, and dispatches the native Pushover request when successful-run Pushover prerequisites are satisfied. Runtime is O(m + c + b) in message count, command length, and Pushover payload size. Side effects include stdout writes, child-process spawning, and outbound HTTPS requests.
|
|
402
584
|
* @param[in] config {PiNotifyConfigFields} Effective notification configuration.
|
|
403
585
|
* @param[in] event {Pick<AgentEndEvent, "messages">} Agent-end payload subset.
|
|
586
|
+
* @param[in] pushoverRequest {PiNotifyPushoverRequest | undefined} Optional successful prompt metadata used for Pushover delivery.
|
|
404
587
|
* @return {void} No return value.
|
|
405
|
-
* @satisfies REQ-129, REQ-130, REQ-131, REQ-132, REQ-133
|
|
588
|
+
* @satisfies REQ-129, REQ-130, REQ-131, REQ-132, REQ-133, REQ-166, REQ-167, REQ-168, REQ-169, REQ-172
|
|
406
589
|
*/
|
|
407
590
|
export function runPiNotifyEffects(
|
|
408
591
|
config: PiNotifyConfigFields,
|
|
409
592
|
event: Pick<AgentEndEvent, "messages">,
|
|
593
|
+
pushoverRequest?: PiNotifyPushoverRequest,
|
|
410
594
|
): void {
|
|
411
595
|
const outcome = classifyPiNotifyOutcome(event);
|
|
412
596
|
const shouldNotify = (
|
|
@@ -420,11 +604,14 @@ export function runPiNotifyEffects(
|
|
|
420
604
|
if (outcome !== "end") {
|
|
421
605
|
return;
|
|
422
606
|
}
|
|
423
|
-
if (config["notify-sound"]
|
|
607
|
+
if (config["notify-sound"] !== "none") {
|
|
608
|
+
runPiNotifySoundCommand(
|
|
609
|
+
config,
|
|
610
|
+
config["notify-sound"] as Exclude<PiNotifySoundLevel, "none">,
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
if (!shouldRunPiNotifyPushover(config, pushoverRequest)) {
|
|
424
614
|
return;
|
|
425
615
|
}
|
|
426
|
-
|
|
427
|
-
config,
|
|
428
|
-
config["notify-sound"] as Exclude<PiNotifySoundLevel, "none">,
|
|
429
|
-
);
|
|
616
|
+
runPiNotifyPushoverRequest(config, pushoverRequest as PiNotifyPushoverRequest);
|
|
430
617
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file
|
|
3
|
-
* @brief Derives runtime-only repository
|
|
4
|
-
* @details Centralizes git-repository probing
|
|
3
|
+
* @brief Derives runtime-only repository facts.
|
|
4
|
+
* @details Centralizes git-repository probing and repository-root resolution for extension status, tool execution, and CLI flows. Runtime is dominated by git subprocess execution plus path normalization. Side effects are limited to subprocess spawning.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
9
|
import { ReqError } from "./errors.js";
|
|
10
|
-
import { isSameOrAncestorPath
|
|
10
|
+
import { isSameOrAncestorPath } from "./path-context.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @brief Executes one git subprocess and captures UTF-8 output.
|
|
@@ -67,33 +67,4 @@ export function resolveRuntimeGitPath(executionPath: string): string | undefined
|
|
|
67
67
|
return isSameOrAncestorPath(gitRoot, normalizedExecutionPath) ? gitRoot : undefined;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* @brief Formats the runtime `base-path` relative to the runtime `git-path`.
|
|
72
|
-
* @details Returns `.` when the repository root is unavailable or identical to the base path. Otherwise returns the slash-normalized relative path from `git-path` to `base-path`. Runtime is O(p) in path length. No external state is mutated.
|
|
73
|
-
* @param[in] basePath {string} Runtime base path.
|
|
74
|
-
* @param[in] gitPath {string | undefined} Runtime repository root.
|
|
75
|
-
* @return {string} Relative base-path token for status rendering.
|
|
76
|
-
* @satisfies REQ-148
|
|
77
|
-
*/
|
|
78
|
-
export function formatBasePathRelativeToGitPath(basePath: string, gitPath: string | undefined): string {
|
|
79
|
-
const normalizedBasePath = path.resolve(basePath);
|
|
80
|
-
if (!gitPath) {
|
|
81
|
-
return ".";
|
|
82
|
-
}
|
|
83
|
-
const normalizedGitPath = path.resolve(gitPath);
|
|
84
|
-
if (normalizedBasePath === normalizedGitPath) {
|
|
85
|
-
return ".";
|
|
86
|
-
}
|
|
87
|
-
return normalizePathSlashes(path.relative(normalizedGitPath, normalizedBasePath)) || ".";
|
|
88
|
-
}
|
|
89
70
|
|
|
90
|
-
/**
|
|
91
|
-
* @brief Formats the runtime git path for status rendering.
|
|
92
|
-
* @details Returns a slash-normalized absolute path or an empty string when no repository root is available. Runtime is O(p) in path length. No external state is mutated.
|
|
93
|
-
* @param[in] gitPath {string | undefined} Runtime repository root.
|
|
94
|
-
* @return {string} Absolute repository path or an empty string.
|
|
95
|
-
* @satisfies REQ-147
|
|
96
|
-
*/
|
|
97
|
-
export function formatAbsoluteGitPath(gitPath: string | undefined): string {
|
|
98
|
-
return gitPath ? normalizePathSlashes(path.resolve(gitPath)) : "";
|
|
99
|
-
}
|
|
@@ -9,12 +9,13 @@ import { Container, SettingsList, Text, type Component, type SettingItem, type S
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @brief Describes one selectable pi-usereq settings-menu choice.
|
|
12
|
-
* @details Stores the stable action identifier, left-column label, right-column current value, and bottom-line description consumed by the shared settings-menu renderer. The interface is compile-time only and introduces no runtime cost.
|
|
12
|
+
* @details Stores the stable action identifier, left-column label, right-column current value, optional value-tone override, and bottom-line description consumed by the shared settings-menu renderer. The interface is compile-time only and introduces no runtime cost.
|
|
13
13
|
*/
|
|
14
14
|
export interface PiUsereqSettingsMenuChoice {
|
|
15
15
|
id: string;
|
|
16
16
|
label: string;
|
|
17
17
|
value: string;
|
|
18
|
+
valueTone?: "default" | "dim";
|
|
18
19
|
description: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -148,12 +149,14 @@ function createImmediateSelectionComponent(choiceId: string, done: (value?: stri
|
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
151
|
* @brief Builds `SettingsList` items from one menu-choice vector.
|
|
151
|
-
* @details Copies labels, current values, and descriptions into `SettingItem` records and attaches a submenu that resolves the outer custom UI with the selected choice identifier. Runtime is O(n) in choice count. No external state is mutated.
|
|
152
|
+
* @details Copies labels, current values, value-tone overrides, and descriptions into `SettingItem` records and attaches a submenu that resolves the outer custom UI with the selected choice identifier. Runtime is O(n) in choice count. No external state is mutated.
|
|
153
|
+
* @param[in] theme {PiUsereqSettingsTheme} Callback-local pi theme adapter.
|
|
152
154
|
* @param[in] choices {PiUsereqSettingsMenuChoice[]} Ordered menu-choice vector.
|
|
153
155
|
* @param[in] done {(value?: string) => void} Outer custom-UI completion callback.
|
|
154
156
|
* @return {SettingItem[]} `SettingsList` item vector.
|
|
155
157
|
*/
|
|
156
158
|
function buildSettingItems(
|
|
159
|
+
theme: PiUsereqSettingsTheme,
|
|
157
160
|
choices: PiUsereqSettingsMenuChoice[],
|
|
158
161
|
done: (value?: string) => void,
|
|
159
162
|
): SettingItem[] {
|
|
@@ -161,7 +164,9 @@ function buildSettingItems(
|
|
|
161
164
|
id: choice.id,
|
|
162
165
|
label: choice.label,
|
|
163
166
|
description: choice.description,
|
|
164
|
-
currentValue: choice.
|
|
167
|
+
currentValue: choice.valueTone === "dim"
|
|
168
|
+
? theme.fg("dim", choice.value)
|
|
169
|
+
: choice.value,
|
|
165
170
|
submenu: () => createImmediateSelectionComponent(choice.id, done),
|
|
166
171
|
}));
|
|
167
172
|
}
|
|
@@ -188,7 +193,7 @@ export async function showPiUsereqSettingsMenu(
|
|
|
188
193
|
0,
|
|
189
194
|
);
|
|
190
195
|
const settingsList = new SettingsList(
|
|
191
|
-
buildSettingItems(choices, done),
|
|
196
|
+
buildSettingItems(theme, choices, done),
|
|
192
197
|
Math.min(Math.max(choices.length, 1), 12),
|
|
193
198
|
buildPiUsereqSettingsListTheme(theme),
|
|
194
199
|
() => undefined,
|