@pi-unipi/unipi 2.14.0 → 2.14.2
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 +18 -0
- package/package.json +23 -23
- package/packages/ask-user/package.json +2 -2
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -0
- package/packages/background-tasks/package.json +2 -2
- package/packages/btw/package.json +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/core/constants.ts +1 -0
- package/packages/core/package.json +1 -1
- package/packages/footer/package.json +3 -3
- package/packages/footer/src/glance-editor.ts +20 -1
- package/packages/footer/src/index.ts +7 -2
- package/packages/footer/src/process-line.ts +6 -2
- package/packages/image/package.json +2 -2
- package/packages/info-screen/index.ts +49 -10
- package/packages/info-screen/package.json +2 -2
- package/packages/info-screen/tui/info-overlay.ts +54 -22
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/memory/package.json +3 -3
- package/packages/milestone/package.json +2 -2
- package/packages/notify/package.json +2 -2
- package/packages/ralph/package.json +3 -3
- package/packages/subagents/package.json +2 -2
- package/packages/trajectory/package.json +1 -1
- package/packages/unipi/bundled.js +6834 -2557
- package/packages/updater/package.json +2 -2
- package/packages/utility/README.md +20 -0
- package/packages/utility/package.json +2 -2
- package/packages/utility/src/commands.ts +54 -0
- package/packages/utility/src/index.ts +15 -0
- package/packages/utility/src/skill-discovery.ts +178 -0
- package/packages/web-api/package.json +2 -2
- package/packages/web-api/src/engine/errors.ts +30 -0
- package/packages/web-api/src/engine/extract.ts +18 -15
- package/packages/web-api/src/providers/wigolo-client.ts +3 -1
- package/packages/web-api/src/tools.ts +6 -5
- package/packages/workflow/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.14.2] — 2026-08-31
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `footer`: **glance frame no longer writes the terminal's last column** (GitHub [#31](https://github.com/Neuron-Mr-White/UniPi/issues/31)) — the glance frame, session strip, process one-liner, and classic status line now cap emitted lines at `width - 1` instead of exactly `width`. pi-tui joins per-tick rewrites with `\r\n`; a line at EXACTLY the terminal width trips auto-wrap on terminals that wrap immediately (or whose glyph widths disagree with `visibleWidth()`, e.g. missing Nerd Fonts / ambiguous-width settings), silently desyncing the differential renderer so every 1s footer refresh painted a fresh input-box copy one block lower until the screen filled. Verified by PTY capture + VT replay: identical byte stream renders 1 stable frame on deferred-wrap terminals vs 12 stacked frames on immediate-wrap ones; the cap removes the footer's contribution entirely. Full resolution of terminal-side drift from pi's own boot/info lines is upstream (pi-tui main-screen wrap-safe bookkeeping).
|
|
14
|
+
- `web-api`: **fetch errors surface their real cause instead of "[object Object]"** — engine errors were plain objects (`{ error, code, phase, retryable }`), so every tool-level catch serialized them via `String()` and both the agent and the user saw only "Read failed: [object Object]" with no diagnosis. `createError()` now returns a real `Error` instance carrying the FetchError fields (interface-compatible: `.error`/`.code`/`.phase`/`.retryable` preserved, classification untouched), and a new `describeError()` normalizes Error instances, FetchError-shaped objects, `{message}`/`{reason}`/`{detail}` shapes, strings, and unserializable values at every tool boundary and in the provider fallthrough. Regression tests cover all shapes; a provider throwing `{ error: "HTTP 429" }` now reports the 429 instead of nothing.
|
|
15
|
+
|
|
16
|
+
## [2.14.1] — 2026-08-28
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `utility`: **skill startup discovery toggle** (`/unipi:skills-settings`, default on) — controls whether Unipi's built-in skills are cataloged in the agent's system prompt at startup. When off (`unipi.skills.discovery: false` in pi settings.json), Unipi's bundled skills are removed from the `<available_skills>` section every turn while user-owned skills (global, project, settings-mounted, third-party packages) stay cataloged; `/skill:name` invocation keeps working since pi expands those commands by reading SKILL.md directly from disk. Filtering is anchor-based (agentskills.io tags) and applied consistently per turn, preserving provider prefix cache.
|
|
21
|
+
- `autocomplete`: registered `/unipi:skills-settings` in COMMAND_REGISTRY + COMMAND_DESCRIPTIONS so the command autosuggests.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `info-screen`: **boot splash no longer eats keystrokes or strands the session** — the auto-close boot dashboard was a capturing overlay during its 2s window: typing during boot was swallowed (worse, its vim-style keys interpreted command text as tab switches), the first-keypress-cancels-auto-close rule left it on screen forever as an unclosable input-eating zombie, and prompts appeared to never send (the agent actually responded behind the overlay). The splash is now non-capturing (typing always reaches the editor) and dismisses itself via a stack-safe `handle.hide()` that only fires while it is the topmost visible overlay entry — if the user opens any dialog during the splash window (e.g. `/unipi:skills-settings`), dismissal defers until it closes, so the overlay can no longer be orphaned with a pending interaction (previously the covered overlay vanished while its `ctx.ui.select` promise hung forever). Interactive (`bootMode: "on"`) dashboards are unchanged; splash footer now reads "auto-dismissing…" instead of keyboard hints that could never reach it.
|
|
26
|
+
|
|
9
27
|
## [2.14.0] — 2026-08-29
|
|
10
28
|
|
|
11
29
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/unipi",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "All-in-one extension suite for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -82,28 +82,28 @@
|
|
|
82
82
|
"typebox": "^1.1.38"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@pi-unipi/ask-user": "2.14.
|
|
86
|
-
"@pi-unipi/background-tasks": "2.14.
|
|
87
|
-
"@pi-unipi/btw": "2.14.
|
|
88
|
-
"@pi-unipi/command-enchantment": "2.14.
|
|
89
|
-
"@pi-unipi/compactor": "2.14.
|
|
90
|
-
"@pi-unipi/core": "2.14.
|
|
91
|
-
"@pi-unipi/footer": "2.14.
|
|
92
|
-
"@pi-unipi/image": "2.14.
|
|
93
|
-
"@pi-unipi/info-screen": "2.14.
|
|
94
|
-
"@pi-unipi/input-shortcuts": "2.14.
|
|
95
|
-
"@pi-unipi/kanboard": "2.14.
|
|
96
|
-
"@pi-unipi/mcp": "2.14.
|
|
97
|
-
"@pi-unipi/memory": "2.14.
|
|
98
|
-
"@pi-unipi/milestone": "2.14.
|
|
99
|
-
"@pi-unipi/notify": "2.14.
|
|
100
|
-
"@pi-unipi/ralph": "2.14.
|
|
101
|
-
"@pi-unipi/subagents": "2.14.
|
|
102
|
-
"@pi-unipi/trajectory": "2.14.
|
|
103
|
-
"@pi-unipi/updater": "2.14.
|
|
104
|
-
"@pi-unipi/utility": "2.14.
|
|
105
|
-
"@pi-unipi/web-api": "2.14.
|
|
106
|
-
"@pi-unipi/workflow": "2.14.
|
|
85
|
+
"@pi-unipi/ask-user": "2.14.2",
|
|
86
|
+
"@pi-unipi/background-tasks": "2.14.2",
|
|
87
|
+
"@pi-unipi/btw": "2.14.2",
|
|
88
|
+
"@pi-unipi/command-enchantment": "2.14.2",
|
|
89
|
+
"@pi-unipi/compactor": "2.14.2",
|
|
90
|
+
"@pi-unipi/core": "2.14.2",
|
|
91
|
+
"@pi-unipi/footer": "2.14.2",
|
|
92
|
+
"@pi-unipi/image": "2.14.2",
|
|
93
|
+
"@pi-unipi/info-screen": "2.14.2",
|
|
94
|
+
"@pi-unipi/input-shortcuts": "2.14.2",
|
|
95
|
+
"@pi-unipi/kanboard": "2.14.2",
|
|
96
|
+
"@pi-unipi/mcp": "2.14.2",
|
|
97
|
+
"@pi-unipi/memory": "2.14.2",
|
|
98
|
+
"@pi-unipi/milestone": "2.14.2",
|
|
99
|
+
"@pi-unipi/notify": "2.14.2",
|
|
100
|
+
"@pi-unipi/ralph": "2.14.2",
|
|
101
|
+
"@pi-unipi/subagents": "2.14.2",
|
|
102
|
+
"@pi-unipi/trajectory": "2.14.2",
|
|
103
|
+
"@pi-unipi/updater": "2.14.2",
|
|
104
|
+
"@pi-unipi/utility": "2.14.2",
|
|
105
|
+
"@pi-unipi/web-api": "2.14.2",
|
|
106
|
+
"@pi-unipi/workflow": "2.14.2"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@earendil-works/pi-agent-core": "^0.84.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/ask-user",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Structured user input tool for Pi coding agent — single-select, multi-select, freeform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@pi-unipi/core": "2.14.
|
|
43
|
+
"@pi-unipi/core": "2.14.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -131,6 +131,7 @@ export const COMMAND_REGISTRY: Record<string, string> = {
|
|
|
131
131
|
"unipi:badge-toggle": "utility",
|
|
132
132
|
"unipi:badge-settings": "utility",
|
|
133
133
|
"unipi:util-settings": "utility",
|
|
134
|
+
"unipi:skills-settings": "utility",
|
|
134
135
|
|
|
135
136
|
// ask-user (1 command)
|
|
136
137
|
"unipi:ask-user-settings": "ask-user",
|
|
@@ -270,6 +271,7 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
|
270
271
|
"unipi:badge-toggle": "Configure badge settings (autoGen, badgeEnabled, agentTool)",
|
|
271
272
|
"unipi:badge-settings": "Configure badge settings via TUI overlay",
|
|
272
273
|
"unipi:util-settings": "Unified settings — badge + diff rendering config",
|
|
274
|
+
"unipi:skills-settings": "Toggle skill startup discovery (skills catalog in system prompt)",
|
|
273
275
|
"unipi:kanboard": "Start the kanboard visualization server",
|
|
274
276
|
"unipi:kanboard-doctor": "Diagnose and fix kanboard parser issues",
|
|
275
277
|
"unipi:trajectory": "Open/reuse trajectory; add stop/off or toggle to close it",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/background-tasks",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Background tasks for UniPi — durable shell jobs, delegated agents, attested Pi runs, and fixed-purpose Fusion workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@earendil-works/pi-ai": "^0.84.0",
|
|
13
|
-
"@pi-unipi/core": "2.14.
|
|
13
|
+
"@pi-unipi/core": "2.14.2",
|
|
14
14
|
"turndown": "^7.2.4"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/btw",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "A pi extension for parallel side conversations with /unipi:btw — part of the Unipi suite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "extensions/btw.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@earendil-works/pi-tui": "^0.84.0"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@pi-unipi/core": "2.14.
|
|
40
|
+
"@pi-unipi/core": "2.14.2"
|
|
41
41
|
},
|
|
42
42
|
"pi": {
|
|
43
43
|
"extensions": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/compactor",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Context engine for Pi — zero-LLM compaction, session continuity, sandbox execution, and tool display optimization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "2.14.
|
|
38
|
-
"@pi-unipi/info-screen": "2.14.
|
|
37
|
+
"@pi-unipi/core": "2.14.2",
|
|
38
|
+
"@pi-unipi/info-screen": "2.14.2",
|
|
39
39
|
"@earendil-works/pi-agent-core": "^0.84.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/footer",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Persistent status bar for Unipi — subscribes to UNIPI_EVENTS and renders key stats from all unipi packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@pi-unipi/core": "2.14.
|
|
36
|
-
"@pi-unipi/background-tasks": "2.14.
|
|
35
|
+
"@pi-unipi/core": "2.14.2",
|
|
36
|
+
"@pi-unipi/background-tasks": "2.14.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -63,6 +63,24 @@ function repeat(s: string, n: number): string {
|
|
|
63
63
|
return s.repeat(Math.max(0, n));
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Frame width for a terminal of `terminalWidth` columns: one column SHORT of
|
|
68
|
+
* the terminal (legacy 8-column floor preserved below 9 columns).
|
|
69
|
+
*
|
|
70
|
+
* Issue #31 invariant — the frame is repainted every second and pi-tui joins
|
|
71
|
+
* the rewritten lines with "\r\n". A line at EXACTLY the terminal width hits
|
|
72
|
+
* the auto-wrap threshold: on terminals that wrap immediately (and whenever
|
|
73
|
+
* a glyph renders wider than visibleWidth() assumed) the cursor silently
|
|
74
|
+
* advances one extra row per line, desyncing the differential renderer —
|
|
75
|
+
* every refresh then paints a fresh frame copy one block lower until the
|
|
76
|
+
* screen fills. Never writing the last column keeps every frame line below
|
|
77
|
+
* the wrap threshold on every terminal.
|
|
78
|
+
*/
|
|
79
|
+
export function glanceFrameWidth(terminalWidth: number): number {
|
|
80
|
+
if (!Number.isFinite(terminalWidth)) return 8;
|
|
81
|
+
return Math.max(8, terminalWidth - 1);
|
|
82
|
+
}
|
|
83
|
+
|
|
66
84
|
function padLine(line: string, width: number): string {
|
|
67
85
|
const w = visibleWidth(line);
|
|
68
86
|
if (w === width) return line;
|
|
@@ -160,7 +178,8 @@ export class GlanceEditor extends CustomEditor {
|
|
|
160
178
|
}
|
|
161
179
|
|
|
162
180
|
override render(width: number): string[] {
|
|
163
|
-
|
|
181
|
+
// One column short of the terminal — see glanceFrameWidth() / issue #31.
|
|
182
|
+
const safe = glanceFrameWidth(width);
|
|
164
183
|
const inner = Math.max(0, safe - 2);
|
|
165
184
|
|
|
166
185
|
// Base render at inner width: harvest content rows only (rules dropped).
|
|
@@ -354,8 +354,11 @@ function setupFooterUI(pi: ExtensionAPI, ctx: ExtensionContext, state: FooterSta
|
|
|
354
354
|
// Hard safety net: never return a line wider than the terminal.
|
|
355
355
|
// This catches any edge cases in layout math or visibleWidth()
|
|
356
356
|
// inconsistencies with PUA characters + ANSI codes.
|
|
357
|
+
// Cap at width - 1 (issue #31): a line at EXACTLY the terminal width
|
|
358
|
+
// trips immediate-wrap terminals and desyncs the diff renderer.
|
|
357
359
|
const line = layout.topContent;
|
|
358
|
-
|
|
360
|
+
const cap = Math.max(1, width - 1);
|
|
361
|
+
return [visibleWidth(line) > cap ? truncateToWidth(line, cap) : line];
|
|
359
362
|
},
|
|
360
363
|
};
|
|
361
364
|
}, { placement: "aboveEditor" });
|
|
@@ -373,7 +376,9 @@ function setupFooterUI(pi: ExtensionAPI, ctx: ExtensionContext, state: FooterSta
|
|
|
373
376
|
if (!strip) return [];
|
|
374
377
|
// Centered under the input box.
|
|
375
378
|
const w = visibleWidth(strip);
|
|
376
|
-
|
|
379
|
+
// Truncate one column short of the terminal (issue #31 — same wrap
|
|
380
|
+
// desync as the glance frame; see glanceFrameWidth()).
|
|
381
|
+
if (w >= width) return [truncateToWidth(strip, Math.max(1, width - 1))];
|
|
377
382
|
const leftPad = Math.floor((width - w) / 2);
|
|
378
383
|
return [" ".repeat(leftPad) + strip];
|
|
379
384
|
},
|
|
@@ -55,7 +55,9 @@ export function countBgProcesses(): BgProcessCounts | null {
|
|
|
55
55
|
* Returns [] when there is nothing to show.
|
|
56
56
|
*/
|
|
57
57
|
export function renderProcessLine(width: number): string[] {
|
|
58
|
-
|
|
58
|
+
// One column can never host a meaningful one-liner, and any content would
|
|
59
|
+
// be exactly-full-width (issue #31 wrap desync) — render nothing.
|
|
60
|
+
if (width <= 1) return [];
|
|
59
61
|
const counts = countBgProcesses();
|
|
60
62
|
if (!counts) return [];
|
|
61
63
|
|
|
@@ -68,7 +70,9 @@ export function renderProcessLine(width: number): string[] {
|
|
|
68
70
|
|
|
69
71
|
const line = parts.join(" ");
|
|
70
72
|
const w = visibleWidth(line);
|
|
71
|
-
|
|
73
|
+
// One column short of the terminal (issue #31 — same wrap desync as the
|
|
74
|
+
// glance frame; see glanceFrameWidth() in glance-editor.ts).
|
|
75
|
+
if (w >= width) return [truncateToWidth(line, Math.max(1, width - 1))];
|
|
72
76
|
const leftPad = Math.floor((width - w) / 2);
|
|
73
77
|
return [" ".repeat(leftPad) + line];
|
|
74
78
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/image",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Image generation and image recognition tools for the Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@pi-unipi/core": "2.14.
|
|
37
|
+
"@pi-unipi/core": "2.14.2"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@earendil-works/pi-ai": "^0.84.0",
|
|
@@ -113,10 +113,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
113
113
|
*/
|
|
114
114
|
function showOverlay(ctx: ExtensionContext, autoCloseMs?: number): void {
|
|
115
115
|
let overlay: InfoOverlay;
|
|
116
|
+
// Splash mode (auto-close): the overlay must NEVER take keyboard focus —
|
|
117
|
+
// it lives exactly during the window where the user starts typing their
|
|
118
|
+
// first prompt. A capturing overlay here eats those keystrokes (worse: its
|
|
119
|
+
// vim-style keys "work", so command text vanishes into tab switches), and
|
|
120
|
+
// its first-keypress-cancels-auto-close rule strands it on screen forever
|
|
121
|
+
// as an unclosable, input-eating zombie. Non-capturing + stack-safe
|
|
122
|
+
// self-dismiss (see startBootTimer) makes it pure eye-candy.
|
|
123
|
+
const splashMode = autoCloseMs !== undefined && autoCloseMs > 0;
|
|
116
124
|
ctx.ui.custom<void>(
|
|
117
125
|
(tui, theme, _keybindings, done) => {
|
|
118
126
|
overlay = new InfoOverlay();
|
|
119
127
|
overlay.setTheme(theme);
|
|
128
|
+
overlay.interactive = !splashMode;
|
|
120
129
|
overlayVisible = true;
|
|
121
130
|
overlay.onClose = () => {
|
|
122
131
|
overlayVisible = false;
|
|
@@ -127,14 +136,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
127
136
|
const component = {
|
|
128
137
|
render: (w: number) => overlay.render(w),
|
|
129
138
|
invalidate: () => overlay.invalidate(),
|
|
139
|
+
// In splash mode the overlay is non-capturing and never receives
|
|
140
|
+
// input; keep handleInput wired for interactive ("on") mode.
|
|
130
141
|
handleInput: (data: string) => {
|
|
131
142
|
overlay.handleInput(data);
|
|
132
143
|
tui.requestRender();
|
|
133
144
|
},
|
|
134
145
|
};
|
|
135
|
-
//
|
|
146
|
+
// Splash self-dismiss may only run while this overlay is the topmost
|
|
147
|
+
// VISIBLE entry of the TUI overlay stack. If the user opens anything
|
|
148
|
+
// during the splash window (a /unipi:… settings dialog, the updater's
|
|
149
|
+
// update prompt, …), dismissal must wait — removing a covered entry
|
|
150
|
+
// breaks the covering overlay (pi retargets focus and orphans its
|
|
151
|
+
// pending interaction, e.g. a hung ctx.ui.select promise). Re-check
|
|
152
|
+
// on every timer tick; once the stack clears we dismiss as usual.
|
|
153
|
+
const isTopmostVisible = (): boolean => {
|
|
154
|
+
try {
|
|
155
|
+
const stack = (tui as unknown as { overlayStack?: Array<{ component?: unknown; hidden?: boolean }> }).overlayStack;
|
|
156
|
+
if (!stack || stack.length === 0) return true;
|
|
157
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
158
|
+
const entry = stack[i];
|
|
159
|
+
if (entry?.hidden) continue;
|
|
160
|
+
return entry?.component === component;
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
} catch {
|
|
164
|
+
return true; // Stack unreadable — assume topmost (legacy behavior).
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
// Boot dashboard dismisses itself (splash mode).
|
|
136
168
|
if (autoCloseMs && autoCloseMs > 0) {
|
|
137
|
-
overlay.startBootTimer(autoCloseMs);
|
|
169
|
+
overlay.startBootTimer(autoCloseMs, isTopmostVisible);
|
|
138
170
|
}
|
|
139
171
|
return component;
|
|
140
172
|
},
|
|
@@ -145,17 +177,24 @@ export default function (pi: ExtensionAPI) {
|
|
|
145
177
|
minWidth: 60,
|
|
146
178
|
anchor: "center" as const,
|
|
147
179
|
margin: 2,
|
|
180
|
+
nonCapturing: splashMode,
|
|
148
181
|
},
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
// until we are the focused (topmost) entry; the user can still press
|
|
156
|
-
// q/Esc to close once the covering overlay is gone.
|
|
182
|
+
// In splash mode the timer uses `selfHide` (handle.hide() splices this
|
|
183
|
+
// entry out by identity) guarded by the topmost-visible check above —
|
|
184
|
+
// never `done()`, which pops whatever is TOPMOST. `isTopmostOverlay`
|
|
185
|
+
// remains as the focused-done() fallback for interactive ("on") mode,
|
|
186
|
+
// where the user drives the dashboard directly: it is topmost while
|
|
187
|
+
// being driven, so the q/Esc → done() path is correct there.
|
|
157
188
|
onHandle: (handle) => {
|
|
158
189
|
overlay.isTopmostOverlay = () => handle.isFocused();
|
|
190
|
+
overlay.selfHide = () => {
|
|
191
|
+
overlayVisible = false;
|
|
192
|
+
if (typeof handle.hide === "function") {
|
|
193
|
+
handle.hide();
|
|
194
|
+
} else if (typeof (handle as { setHidden?: (v: boolean) => void }).setHidden === "function") {
|
|
195
|
+
(handle as { setHidden: (v: boolean) => void }).setHidden(true);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
159
198
|
},
|
|
160
199
|
}
|
|
161
200
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/info-screen",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Dashboard and module registry for Unipi — configurable info overlay with tabbed groups",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "2.14.
|
|
36
|
+
"@pi-unipi/core": "2.14.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -69,6 +69,21 @@ export class InfoOverlay implements Component {
|
|
|
69
69
|
* `startBootTimer` for why.
|
|
70
70
|
*/
|
|
71
71
|
isTopmostOverlay?: () => boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Stack-safe self-removal via the overlay handle (`handle.hide()` splices
|
|
74
|
+
* this entry out of the TUI stack by identity, unlike `done()` which pops
|
|
75
|
+
* whatever is TOPMOST). Set from `onHandle` in index.ts. When available,
|
|
76
|
+
* the boot auto-close timer prefers this over `done()` so a splash that
|
|
77
|
+
* lingers while another overlay opens can never dismiss that overlay
|
|
78
|
+
* instead of itself.
|
|
79
|
+
*/
|
|
80
|
+
selfHide?: () => void;
|
|
81
|
+
/**
|
|
82
|
+
* False when running as a non-capturing boot splash (auto-close): the
|
|
83
|
+
* overlay never receives keyboard input, so interactive hints like
|
|
84
|
+
* "q/Esc close" would be misleading and are replaced accordingly.
|
|
85
|
+
*/
|
|
86
|
+
interactive = true;
|
|
72
87
|
|
|
73
88
|
private overlay = new OverlayTheme();
|
|
74
89
|
|
|
@@ -232,31 +247,46 @@ export class InfoOverlay implements Component {
|
|
|
232
247
|
}
|
|
233
248
|
|
|
234
249
|
/**
|
|
235
|
-
* Auto-close the overlay after `ms
|
|
250
|
+
* Auto-close the overlay after `ms`.
|
|
236
251
|
*
|
|
237
|
-
* Used when the overlay is shown
|
|
238
|
-
* it should get out of the way on its own rather than
|
|
252
|
+
* Used when the overlay is shown as a boot splash: the dashboard is
|
|
253
|
+
* informational, so it should get out of the way on its own rather than
|
|
254
|
+
* requiring a keypress.
|
|
239
255
|
*
|
|
240
|
-
*
|
|
241
|
-
* TUI stack
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
256
|
+
* Dismissal uses `selfHide` (handle.hide() — removes THIS entry from the
|
|
257
|
+
* TUI overlay stack by identity) and only fires while `isTopmostVisible`
|
|
258
|
+
* confirms nothing is stacked above: dismissing a covered overlay breaks
|
|
259
|
+
* the covering one (pi retargets focus and orphans its pending
|
|
260
|
+
* interaction, e.g. a ctx.ui.select promise that never resolves while its
|
|
261
|
+
* overlay vanishes). If covered, the timer re-arms and retries.
|
|
262
|
+
*
|
|
263
|
+
* If `selfHide` is unavailable (older host), falls back to the guarded
|
|
264
|
+
* `onClose` (`done()`) path, which requires focus (topmost) for the same
|
|
265
|
+
* reason.
|
|
248
266
|
*/
|
|
249
|
-
startBootTimer(ms: number): void {
|
|
267
|
+
startBootTimer(ms: number, isTopmostVisible?: () => boolean): void {
|
|
250
268
|
this.cancelBootTimer();
|
|
251
269
|
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
252
270
|
const arm = (): void => {
|
|
253
271
|
this.bootTimer = setTimeout(() => {
|
|
254
272
|
this.bootTimer = null;
|
|
255
273
|
if (this._destroyed) return;
|
|
274
|
+
if (this.selfHide) {
|
|
275
|
+
if (isTopmostVisible && !isTopmostVisible()) {
|
|
276
|
+
// Something is stacked on top of us — dismissing now would break
|
|
277
|
+
// it (orphaned select promise, focus retarget). Retry shortly;
|
|
278
|
+
// once the stack clears we dismiss as usual.
|
|
279
|
+
arm();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
this.selfHide();
|
|
283
|
+
this.destroy();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
256
286
|
if (this.isTopmostOverlay && !this.isTopmostOverlay()) {
|
|
257
|
-
//
|
|
258
|
-
// instead of this dashboard. Retry shortly; once
|
|
259
|
-
//
|
|
287
|
+
// Fallback (no selfHide available): closing now would pop the
|
|
288
|
+
// covering overlay instead of this dashboard. Retry shortly; once
|
|
289
|
+
// we are topmost the close is safe.
|
|
260
290
|
arm();
|
|
261
291
|
return;
|
|
262
292
|
}
|
|
@@ -346,7 +376,7 @@ export class InfoOverlay implements Component {
|
|
|
346
376
|
lines.push(this.overlay.frameLine(this.overlay.fg("dim", "Modules will register groups on startup."), innerWidth));
|
|
347
377
|
for (let i = 0; i < 4; i++) lines.push(this.overlay.frameLine("", innerWidth));
|
|
348
378
|
lines.push(this.overlay.ruleLine(innerWidth));
|
|
349
|
-
lines.push(this.overlay.frameLine(this.overlay.fg("dim", "q/Esc close · r refresh"), innerWidth));
|
|
379
|
+
lines.push(this.overlay.frameLine(this.overlay.fg("dim", this.interactive ? "q/Esc close · r refresh" : "auto-dismissing…"), innerWidth));
|
|
350
380
|
lines.push(this.overlay.borderLine(innerWidth, "bottom"));
|
|
351
381
|
return lines;
|
|
352
382
|
}
|
|
@@ -529,12 +559,14 @@ export class InfoOverlay implements Component {
|
|
|
529
559
|
const lastUp = infoRegistry.getLastUpdated(group?.id ?? "");
|
|
530
560
|
const age = lastUp > 0 ? humanizeAge(Date.now() - lastUp) : "loading…";
|
|
531
561
|
|
|
532
|
-
const hints =
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
562
|
+
const hints = this.interactive
|
|
563
|
+
? [
|
|
564
|
+
`${this.overlay.fg("accent", "←/→")} tabs`,
|
|
565
|
+
`${this.overlay.fg("success", "↑/↓")} scroll`,
|
|
566
|
+
`${this.overlay.fg("warning", "r")} refresh`,
|
|
567
|
+
`${this.overlay.fg("error", "q/Esc")} close`,
|
|
568
|
+
]
|
|
569
|
+
: [`${this.overlay.fg("dim", "auto-dismissing…")}`];
|
|
538
570
|
|
|
539
571
|
const hintStr = hints.join(` ${this.overlay.fg("borderMuted", "•")} `);
|
|
540
572
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/input-shortcuts",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Keyboard shortcuts for stash/restore, undo/redo, clipboard, and thinking toggle — chord-based overlay system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pi-unipi/core": "2.14.
|
|
36
|
+
"@pi-unipi/core": "2.14.2"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/kanboard",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Visualization layer for unipi workflow — HTTP server with htmx/Alpine.js UI, modular parsers, TUI overlay, and kanban board",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@pi-unipi/core": "2.14.
|
|
42
|
+
"@pi-unipi/core": "2.14.2"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/mcp",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "MCP server management extension for Pi coding agent — browse, add, configure, and use MCP servers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test": "npx tsx --test tests/**/*.test.ts"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@pi-unipi/core": "2.14.
|
|
33
|
+
"@pi-unipi/core": "2.14.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/memory",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Persistent cross-session memory with MemPalace backend (auto-installed) and SQLite fallback for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"README.md"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@pi-unipi/core": "2.14.
|
|
43
|
-
"@pi-unipi/info-screen": "2.14.
|
|
42
|
+
"@pi-unipi/core": "2.14.2",
|
|
43
|
+
"@pi-unipi/info-screen": "2.14.2",
|
|
44
44
|
"js-yaml": "^4.1.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|