pi-usereq 0.6.0 → 0.7.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/CHANGELOG.md +38 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/pi-usereq/docs/REFERENCES.md +24470 -4561
- package/pi-usereq/docs/REQUIREMENTS.md +70 -50
- package/pi-usereq/docs/WORKFLOW.md +112 -77
- package/src/core/config.ts +84 -29
- package/src/core/extension-status.ts +49 -177
- package/src/core/pi-notify.ts +473 -313
- package/src/core/settings-menu.ts +21 -1
- package/src/core/token-counter.ts +55 -3
- package/src/index.ts +735 -438
- package/src/resources/images/favicon.svg +21 -0
- package/src/resources/images/pi.dev.png +0 -0
- package/tests/debug-extension-harness.test.ts +6 -6
- package/tests/extension-registration.test.ts +609 -129
|
@@ -17,7 +17,6 @@ import type {
|
|
|
17
17
|
} from "@mariozechner/pi-coding-agent";
|
|
18
18
|
import type { UseReqConfig } from "./config.js";
|
|
19
19
|
import { normalizePathSlashes } from "./path-context.js";
|
|
20
|
-
import { formatPiNotifyBeepStatus, formatPiNotifyPushoverStatus } from "./pi-notify.js";
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* @brief Enumerates the CLI-supported theme tokens consumed by status rendering.
|
|
@@ -32,42 +31,24 @@ type StatusForegroundColor = Extract<
|
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* @brief Describes the raw theme capabilities required for status rendering.
|
|
35
|
-
* @details Accepts the `ctx.ui.theme` foreground renderer
|
|
36
|
-
*
|
|
37
|
-
* context-usage bar. Compile-time only and introduces no runtime cost.
|
|
34
|
+
* @details Accepts the `ctx.ui.theme` foreground renderer used by the
|
|
35
|
+
* single-line footer. Compile-time only and introduces no runtime cost.
|
|
38
36
|
*/
|
|
39
37
|
interface RawStatusTheme {
|
|
40
38
|
fg: (color: StatusForegroundColor, text: string) => string;
|
|
41
|
-
bgFromFg?: (color: StatusForegroundColor, text: string) => string;
|
|
42
|
-
getFgAnsi?: (color: StatusForegroundColor) => string;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/**
|
|
46
42
|
* @brief Describes the normalized theme adapter used by status formatters.
|
|
47
|
-
* @details Exposes deterministic label, value, foreground,
|
|
48
|
-
*
|
|
49
|
-
*
|
|
43
|
+
* @details Exposes deterministic label, value, foreground, and separator
|
|
44
|
+
* renderers so status text generation stays independent from the raw theme API.
|
|
45
|
+
* Compile-time only and introduces no runtime cost.
|
|
50
46
|
*/
|
|
51
47
|
interface StatusThemeAdapter {
|
|
52
48
|
label: (fieldName: string) => string;
|
|
53
49
|
value: (text: string) => string;
|
|
54
50
|
colorize: (color: StatusForegroundColor, text: string) => string;
|
|
55
|
-
backgroundize: (color: StatusForegroundColor, text: string) => string;
|
|
56
51
|
separator: string;
|
|
57
|
-
filledContextCell: string;
|
|
58
|
-
emptyContextCell: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* @brief Describes one fixed-width context-bar overlay.
|
|
63
|
-
* @details Stores the literal text plus foreground and background color roles
|
|
64
|
-
* used when the context bar must render threshold-specific labels instead of
|
|
65
|
-
* block glyphs. Compile-time only and introduces no runtime cost.
|
|
66
|
-
*/
|
|
67
|
-
interface ContextUsageOverlaySpec {
|
|
68
|
-
backgroundColor: StatusForegroundColor;
|
|
69
|
-
foregroundColor: StatusForegroundColor;
|
|
70
|
-
text: "◀ CLEAR ▶ " | " ◀ FULL ▶ ";
|
|
71
52
|
}
|
|
72
53
|
|
|
73
54
|
/**
|
|
@@ -149,90 +130,31 @@ export interface PiUsereqStatusController {
|
|
|
149
130
|
tickHandle: ReturnType<typeof setInterval> | undefined;
|
|
150
131
|
}
|
|
151
132
|
|
|
152
|
-
/**
|
|
153
|
-
* @brief Converts a foreground ANSI sequence into the equivalent background ANSI.
|
|
154
|
-
* @details Supports the standard `38;` foreground prefix emitted by pi themes.
|
|
155
|
-
* Returns `undefined` when the input cannot be transformed deterministically.
|
|
156
|
-
* Runtime is O(n) in ANSI sequence length. No external state is mutated.
|
|
157
|
-
* @param[in] foregroundAnsi {string} Foreground ANSI sequence.
|
|
158
|
-
* @return {string | undefined} Background ANSI sequence when derivable.
|
|
159
|
-
*/
|
|
160
|
-
function convertForegroundAnsiToBackgroundAnsi(
|
|
161
|
-
foregroundAnsi: string,
|
|
162
|
-
): string | undefined {
|
|
163
|
-
if (!foregroundAnsi.includes("\u001b[")) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
const backgroundAnsi = foregroundAnsi.replace("[38;", "[48;");
|
|
167
|
-
return backgroundAnsi === foregroundAnsi ? undefined : backgroundAnsi;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* @brief Applies a foreground-derived background style to one text fragment.
|
|
172
|
-
* @details Prefers a theme-provided `bgFromFg` encoder for deterministic test
|
|
173
|
-
* rendering and falls back to ANSI conversion when the runtime theme exposes
|
|
174
|
-
* `getFgAnsi`. Runtime is O(n) in fragment length. No external state is
|
|
175
|
-
* mutated.
|
|
176
|
-
* @param[in] theme {RawStatusTheme} Raw theme adapter.
|
|
177
|
-
* @param[in] color {StatusForegroundColor} Foreground color reused as background.
|
|
178
|
-
* @param[in] text {string} Already-colored foreground fragment.
|
|
179
|
-
* @return {string} Background-decorated text fragment.
|
|
180
|
-
*/
|
|
181
|
-
function applyForegroundAsBackground(
|
|
182
|
-
theme: RawStatusTheme,
|
|
183
|
-
color: StatusForegroundColor,
|
|
184
|
-
text: string,
|
|
185
|
-
): string {
|
|
186
|
-
if (typeof theme.bgFromFg === "function") {
|
|
187
|
-
return theme.bgFromFg(color, text);
|
|
188
|
-
}
|
|
189
|
-
if (typeof theme.getFgAnsi === "function") {
|
|
190
|
-
const backgroundAnsi = convertForegroundAnsiToBackgroundAnsi(
|
|
191
|
-
theme.getFgAnsi(color),
|
|
192
|
-
);
|
|
193
|
-
if (backgroundAnsi) {
|
|
194
|
-
return `${backgroundAnsi}${text}\u001b[39m\u001b[49m`;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return text;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
133
|
/**
|
|
201
134
|
* @brief Builds the normalized theme adapter used by pi-usereq status formatters.
|
|
202
|
-
* @details Precomputes label, value, foreground,
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
* is mutated.
|
|
135
|
+
* @details Precomputes label, value, foreground, and separator renderers so
|
|
136
|
+
* status formatting remains stable across real TUI themes and deterministic
|
|
137
|
+
* test doubles. Runtime is O(1). No external state is mutated.
|
|
206
138
|
* @param[in] theme {RawStatusTheme} Raw theme implementation from `ctx.ui.theme`.
|
|
207
139
|
* @return {StatusThemeAdapter} Normalized status-theme adapter.
|
|
208
140
|
*/
|
|
209
141
|
function createStatusThemeAdapter(theme: RawStatusTheme): StatusThemeAdapter {
|
|
210
142
|
const colorize = (color: StatusForegroundColor, text: string): string =>
|
|
211
143
|
theme.fg(color, text);
|
|
212
|
-
const backgroundize = (color: StatusForegroundColor, text: string): string =>
|
|
213
|
-
applyForegroundAsBackground(theme, color, text);
|
|
214
144
|
return {
|
|
215
145
|
label: (fieldName: string): string => colorize("accent", `${fieldName}:`),
|
|
216
146
|
value: (text: string): string => colorize("warning", text),
|
|
217
147
|
colorize,
|
|
218
|
-
backgroundize,
|
|
219
148
|
separator: colorize("dim", " • "),
|
|
220
|
-
filledContextCell: backgroundize(
|
|
221
|
-
"accent",
|
|
222
|
-
colorize("warning", "▓"),
|
|
223
|
-
),
|
|
224
|
-
emptyContextCell: backgroundize(
|
|
225
|
-
"accent",
|
|
226
|
-
colorize("dim", "▓"),
|
|
227
|
-
),
|
|
228
149
|
};
|
|
229
150
|
}
|
|
230
151
|
|
|
231
152
|
/**
|
|
232
153
|
* @brief Normalizes one raw context-usage snapshot.
|
|
233
154
|
* @details Preserves the runtime token and context-window counts, derives a
|
|
234
|
-
* percentage when the runtime omits it,
|
|
235
|
-
* Runtime is O(1). No
|
|
155
|
+
* percentage when the runtime omits it, clamps negative percentages to `0`,
|
|
156
|
+
* and preserves overflow above `100` for footer rendering. Runtime is O(1). No
|
|
157
|
+
* external state is mutated.
|
|
236
158
|
* @param[in] contextUsage {ContextUsage | undefined} Raw runtime snapshot.
|
|
237
159
|
* @return {ContextUsage | undefined} Normalized snapshot.
|
|
238
160
|
*/
|
|
@@ -251,7 +173,7 @@ function normalizeContextUsage(
|
|
|
251
173
|
...contextUsage,
|
|
252
174
|
percent: derivedPercent === null
|
|
253
175
|
? null
|
|
254
|
-
: Math.max(0,
|
|
176
|
+
: Math.max(0, derivedPercent),
|
|
255
177
|
};
|
|
256
178
|
}
|
|
257
179
|
|
|
@@ -273,100 +195,52 @@ function refreshContextUsage(
|
|
|
273
195
|
}
|
|
274
196
|
|
|
275
197
|
/**
|
|
276
|
-
* @brief
|
|
277
|
-
* @details
|
|
278
|
-
*
|
|
279
|
-
* O(1). No external state is mutated.
|
|
280
|
-
* @param[in] contextUsage {ContextUsage | undefined} Normalized context snapshot.
|
|
281
|
-
* @return {number} Filled-cell count in the inclusive range `[0, 10]`.
|
|
282
|
-
* @satisfies REQ-122
|
|
283
|
-
*/
|
|
284
|
-
function countFilledContextCells(
|
|
285
|
-
contextUsage: ContextUsage | undefined,
|
|
286
|
-
): number {
|
|
287
|
-
const percent = contextUsage?.percent;
|
|
288
|
-
if (percent === null || percent === undefined || percent <= 0) {
|
|
289
|
-
return 0;
|
|
290
|
-
}
|
|
291
|
-
return Math.min(10, Math.ceil((percent * 10) / 100));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* @brief Resolves the threshold-specific context-bar overlay when required.
|
|
296
|
-
* @details Returns the empty-state `◀ CLEAR ▶ ` overlay when normalized context
|
|
297
|
-
* usage is unavailable or non-positive and returns the centered ` ◀ FULL ▶ `
|
|
298
|
-
* overlay with the active theme `error` token when usage exceeds 90 percent.
|
|
299
|
-
* Runtime is O(1). No external state is mutated.
|
|
198
|
+
* @brief Resolves the icon text for one normalized context-usage snapshot.
|
|
199
|
+
* @details Maps context usage to one fixed-width icon band so footer rendering
|
|
200
|
+
* remains compact and deterministic. Unavailable usage degrades to the `0%`
|
|
201
|
+
* icon. Runtime is O(1). No external state is mutated.
|
|
300
202
|
* @param[in] contextUsage {ContextUsage | undefined} Normalized context snapshot.
|
|
301
|
-
* @return {
|
|
302
|
-
* @satisfies REQ-
|
|
203
|
+
* @return {string} Fixed-width gauge icon text.
|
|
204
|
+
* @satisfies REQ-121, REQ-122
|
|
303
205
|
*/
|
|
304
|
-
function
|
|
206
|
+
function resolveContextUsageIconText(
|
|
305
207
|
contextUsage: ContextUsage | undefined,
|
|
306
|
-
):
|
|
208
|
+
): string {
|
|
307
209
|
const percent = contextUsage?.percent;
|
|
308
210
|
if (percent === undefined || percent === null || percent <= 0) {
|
|
309
|
-
return
|
|
310
|
-
backgroundColor: "accent",
|
|
311
|
-
foregroundColor: "warning",
|
|
312
|
-
text: "◀ CLEAR ▶ ",
|
|
313
|
-
};
|
|
211
|
+
return "▕_▏";
|
|
314
212
|
}
|
|
315
|
-
if (percent
|
|
316
|
-
return
|
|
317
|
-
backgroundColor: "warning",
|
|
318
|
-
foregroundColor: "error",
|
|
319
|
-
text: " ◀ FULL ▶ ",
|
|
320
|
-
};
|
|
213
|
+
if (percent <= 25) {
|
|
214
|
+
return "▕▂▏";
|
|
321
215
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
* and state-specific backdrop remain preserved. Runtime is O(n) in overlay
|
|
330
|
-
* width. No external state is mutated.
|
|
331
|
-
* @param[in] theme {StatusThemeAdapter} Normalized status theme.
|
|
332
|
-
* @param[in] overlay {ContextUsageOverlaySpec} Overlay specification.
|
|
333
|
-
* @return {string} Rendered overlay text.
|
|
334
|
-
* @satisfies REQ-127, REQ-128
|
|
335
|
-
*/
|
|
336
|
-
function formatContextUsageOverlay(
|
|
337
|
-
theme: StatusThemeAdapter,
|
|
338
|
-
overlay: ContextUsageOverlaySpec,
|
|
339
|
-
): string {
|
|
340
|
-
return theme.backgroundize(
|
|
341
|
-
overlay.backgroundColor,
|
|
342
|
-
theme.colorize(overlay.foregroundColor, overlay.text),
|
|
343
|
-
);
|
|
216
|
+
if (percent <= 50) {
|
|
217
|
+
return "▕▄▏";
|
|
218
|
+
}
|
|
219
|
+
if (percent <= 90) {
|
|
220
|
+
return "▕▆▏";
|
|
221
|
+
}
|
|
222
|
+
return "▕█▏";
|
|
344
223
|
}
|
|
345
224
|
|
|
346
225
|
/**
|
|
347
|
-
* @brief Formats one
|
|
348
|
-
* @details Renders
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* mutated.
|
|
226
|
+
* @brief Formats one icon-based context-usage gauge.
|
|
227
|
+
* @details Renders the documented `warning`-colored icon bands for `0-100%` and
|
|
228
|
+
* applies terminal blink control to the `error`-colored overflow icon so
|
|
229
|
+
* unsupported terminals degrade to non-blinking red automatically. Runtime is
|
|
230
|
+
* O(1). No external state is mutated.
|
|
353
231
|
* @param[in] theme {StatusThemeAdapter} Normalized status theme.
|
|
354
232
|
* @param[in] contextUsage {ContextUsage | undefined} Normalized context snapshot.
|
|
355
|
-
* @return {string} Rendered
|
|
233
|
+
* @return {string} Rendered fixed-width gauge icon.
|
|
356
234
|
* @satisfies REQ-121, REQ-122, REQ-126, REQ-127, REQ-128
|
|
357
235
|
*/
|
|
358
236
|
function formatContextUsageBar(
|
|
359
237
|
theme: StatusThemeAdapter,
|
|
360
238
|
contextUsage: ContextUsage | undefined,
|
|
361
239
|
): string {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
return formatContextUsageOverlay(theme, overlay);
|
|
240
|
+
if ((contextUsage?.percent ?? 0) > 100) {
|
|
241
|
+
return theme.colorize("error", "\u001b[5m▕█▏\u001b[25m");
|
|
365
242
|
}
|
|
366
|
-
|
|
367
|
-
return Array.from({ length: 10 }, (_value, index) =>
|
|
368
|
-
index < filledCells ? theme.filledContextCell : theme.emptyContextCell,
|
|
369
|
-
).join("");
|
|
243
|
+
return theme.value(resolveContextUsageIconText(contextUsage));
|
|
370
244
|
}
|
|
371
245
|
|
|
372
246
|
/**
|
|
@@ -404,7 +278,7 @@ function formatCompletedStatusDuration(
|
|
|
404
278
|
* @brief Formats the consolidated `elapsed` status-bar value.
|
|
405
279
|
* @details Emits the active prompt segment `⏱︎ <active>`, the latest normally
|
|
406
280
|
* completed segment `⚑ <last>`, and the accumulated successful-runtime segment
|
|
407
|
-
*
|
|
281
|
+
* `⌛︎<total>` with fixed spacing. Runtime is O(1). No external state is
|
|
408
282
|
* mutated.
|
|
409
283
|
* @param[in] state {PiUsereqStatusState} Mutable status state snapshot.
|
|
410
284
|
* @param[in] nowMs {number} Current wall-clock time in milliseconds.
|
|
@@ -420,7 +294,7 @@ function formatElapsedStatusValue(
|
|
|
420
294
|
: formatStatusDuration(nowMs - state.runStartTimeMs);
|
|
421
295
|
const lastText = formatCompletedStatusDuration(state.lastRunDurationMs);
|
|
422
296
|
const totalText = formatCompletedStatusDuration(state.totalRunDurationMs);
|
|
423
|
-
return
|
|
297
|
+
return `⏱︎ ${activeText} ⚑ ${lastText} ⌛︎${totalText}`;
|
|
424
298
|
}
|
|
425
299
|
|
|
426
300
|
/**
|
|
@@ -478,14 +352,16 @@ function didAgentEndAbort(messages: AgentEndEvent["messages"]): boolean {
|
|
|
478
352
|
|
|
479
353
|
/**
|
|
480
354
|
* @brief Builds the full single-line pi-usereq status-bar payload.
|
|
481
|
-
* @details Renders base, context, elapsed,
|
|
355
|
+
* @details Renders base, context, elapsed, and sound fields in the canonical
|
|
356
|
+
* order with dim bullet separators and the documented icon-based context gauge.
|
|
357
|
+
* Runtime is O(1). No external state is mutated.
|
|
482
358
|
* @param[in] cwd {string} Runtime working directory used for base-path derivation.
|
|
483
359
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
484
360
|
* @param[in] theme {StatusThemeAdapter} Normalized status theme.
|
|
485
361
|
* @param[in] state {PiUsereqStatusState} Mutable status state snapshot.
|
|
486
362
|
* @param[in] nowMs {number} Current wall-clock time in milliseconds.
|
|
487
363
|
* @return {string} Single-line status-bar text.
|
|
488
|
-
* @satisfies REQ-109, REQ-112, REQ-120, REQ-121, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-
|
|
364
|
+
* @satisfies REQ-109, REQ-112, REQ-120, REQ-121, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-148, REQ-156, REQ-159, REQ-180
|
|
489
365
|
*/
|
|
490
366
|
function buildPiUsereqStatusText(
|
|
491
367
|
cwd: string,
|
|
@@ -496,9 +372,7 @@ function buildPiUsereqStatusText(
|
|
|
496
372
|
): string {
|
|
497
373
|
const baseText = normalizePathSlashes(path.resolve(cwd));
|
|
498
374
|
const elapsedText = formatElapsedStatusValue(state, nowMs);
|
|
499
|
-
const beepText = formatPiNotifyBeepStatus(config);
|
|
500
375
|
const soundText = config["notify-sound"];
|
|
501
|
-
const pushoverText = formatPiNotifyPushoverStatus(config);
|
|
502
376
|
return [
|
|
503
377
|
formatStatusField(theme, "base", baseText),
|
|
504
378
|
formatRenderedStatusField(
|
|
@@ -507,9 +381,7 @@ function buildPiUsereqStatusText(
|
|
|
507
381
|
formatContextUsageBar(theme, state.contextUsage),
|
|
508
382
|
),
|
|
509
383
|
formatStatusField(theme, "elapsed", elapsedText),
|
|
510
|
-
formatStatusField(theme, "beep", beepText),
|
|
511
384
|
formatStatusField(theme, "sound", soundText),
|
|
512
|
-
formatStatusField(theme, "pushover", pushoverText),
|
|
513
385
|
].join(theme.separator);
|
|
514
386
|
}
|
|
515
387
|
|
|
@@ -598,13 +470,13 @@ export function setPiUsereqStatusConfig(
|
|
|
598
470
|
/**
|
|
599
471
|
* @brief Renders the current pi-usereq status bar into the active UI context.
|
|
600
472
|
* @details Updates the controller's latest context pointer and writes the
|
|
601
|
-
* single-line status text only when configuration is available, including
|
|
602
|
-
*
|
|
603
|
-
*
|
|
473
|
+
* single-line status text only when configuration is available, including the
|
|
474
|
+
* documented icon-based context gauge. Runtime is O(s) in configured source-
|
|
475
|
+
* path count. Side effect: mutates `ctx.ui` status.
|
|
604
476
|
* @param[in,out] controller {PiUsereqStatusController} Mutable status controller.
|
|
605
477
|
* @param[in] ctx {ExtensionContext} Active extension context.
|
|
606
478
|
* @return {void} No return value.
|
|
607
|
-
* @satisfies REQ-120, REQ-121, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-
|
|
479
|
+
* @satisfies REQ-120, REQ-121, REQ-123, REQ-124, REQ-125, REQ-126, REQ-127, REQ-128, REQ-148, REQ-159, REQ-180
|
|
608
480
|
*/
|
|
609
481
|
export function renderPiUsereqStatus(
|
|
610
482
|
controller: PiUsereqStatusController,
|