@pi-unipi/footer 3.0.0-alpha.3 → 3.0.0-alpha.30
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 +66 -125
- package/index.ts +0 -0
- package/package.json +3 -3
- package/src/commands.ts +12 -41
- package/src/config.ts +164 -207
- package/src/events.ts +40 -247
- package/src/glance-editor.ts +133 -40
- package/src/index.ts +195 -346
- package/src/registry/index.ts +4 -53
- package/src/rendering/icons.ts +18 -21
- package/src/segments/compactor.ts +27 -168
- package/src/segments/long-horizon.ts +2 -35
- package/src/session-scan.ts +271 -0
- package/src/strip.ts +238 -0
- package/src/types.ts +48 -135
- package/src/help.ts +0 -160
- package/src/presets.ts +0 -129
- package/src/rendering/renderer.ts +0 -454
- package/src/rendering/separators.ts +0 -102
- package/src/segments/core.ts +0 -412
- package/src/segments/kanboard.ts +0 -91
- package/src/segments/mcp.ts +0 -109
- package/src/segments/memory.ts +0 -168
- package/src/segments/notify.ts +0 -61
- package/src/segments/ralph.ts +0 -123
- package/src/segments/status-ext.ts +0 -124
- package/src/segments/workflow.ts +0 -124
package/README.md
CHANGED
|
@@ -1,149 +1,90 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Footer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Frames the input box and shows live session stats at the bottom of the terminal.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`@pi-unipi/footer` · part of [UniPi](../../README.md)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## What it does
|
|
10
10
|
|
|
11
|
+
- Puts a frame around the input box (**the glance frame**, always on when the footer is enabled).
|
|
12
|
+
- Shows a stats line below the input: input/output tokens, cost, average time to first token (TTFT), tokens per second, turns, steps, model and tool time, cache hit rate, and compactions.
|
|
13
|
+
- Shows a line above the input with counts of background tasks: running, stopped, failed and done.
|
|
14
|
+
- Adapts to the terminal: parts of the stats line drop by priority when the terminal is narrow, badges leave the frame titles before the branch or model truncate, and on very short terminals the stats and task lines hide.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
UniPi installs this package:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pi install npm:@pi-unipi/unipi
|
|
11
22
|
```
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
|
|
24
|
+
To install this package alone:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pi install npm:@pi-unipi/footer
|
|
16
28
|
```
|
|
17
29
|
|
|
18
|
-
|
|
19
|
-
- **Bottom border:** workspace · context %/window · model · thinking level
|
|
20
|
-
- **Session strip:** turns/steps, wall + tool wall time, average TTFT, tok/s, cache hit % — colored per stat, honest across restarts (derived from persisted session timestamps when live hooks are unavailable; provider-reported `usage.output` anchors token counts whenever present)
|
|
21
|
-
- **Process line (new in 2.13):** centered one-liner directly above the frame while background work is in flight — `● 3 running ● 1 stopped ● 1 failed ● 2 done` — green ● running, yellow ● stopped (killed), red ● failed, gray ● done. Covers every task type (shell jobs, delegates, fusion workflows) via direct registry reads; zero-count buckets are omitted and the line hides when idle. Counts reset per session.
|
|
22
|
-
- The classic segment status line is suppressed while glance mode is on; toggle it back for the classic footer
|
|
30
|
+
The footer starts with the session. Open `/unipi:settings` → **Footer** to change it.
|
|
23
31
|
|
|
24
32
|
## Commands
|
|
25
33
|
|
|
26
|
-
| Command |
|
|
27
|
-
|
|
28
|
-
| `/unipi:footer` |
|
|
29
|
-
| `/unipi:footer on`
|
|
30
|
-
| `/unipi:
|
|
31
|
-
| `/unipi:footer-help` | Show footer segment guide |
|
|
32
|
-
|
|
33
|
-
## Special Triggers
|
|
34
|
-
|
|
35
|
-
Footer subscribes to events from every Unipi package:
|
|
36
|
-
|
|
37
|
-
| Group | Events | Segments |
|
|
38
|
-
|-------|--------|----------|
|
|
39
|
-
| core | Pi SDK | model, thinking, path, git, context_pct, cost, tokens, session |
|
|
40
|
-
| compactor | Pi session data + `COMPACTOR_COMPACTED` | session_events, compactions, tokens_saved, compression_ratio |
|
|
41
|
-
| memory | `MEMORY_STORED`/`DELETED`/`CONSOLIDATED` | project_count, total_count, consolidations |
|
|
42
|
-
| mcp | `MCP_SERVER_STARTED`/`STOPPED`/`ERROR` | servers_total, servers_active, tools_total |
|
|
43
|
-
| ralph | `RALPH_LOOP_START`/`END`/`ITERATION_DONE` | active_loops, total_iterations, loop_status |
|
|
44
|
-
| workflow | `WORKFLOW_START`/`END` | current_command, sandbox_level, command_duration |
|
|
45
|
-
| kanboard | Direct registry read | docs_count, tasks_done, tasks_total, task_pct |
|
|
46
|
-
| notify | `NOTIFICATION_SENT` | platforms_enabled, last_sent |
|
|
47
|
-
|
|
48
|
-
Footer works even if packages load after it — late-arriving events update the cache.
|
|
49
|
-
|
|
50
|
-
## Presets
|
|
51
|
-
|
|
52
|
-
| Preset | Description |
|
|
53
|
-
|--------|-------------|
|
|
54
|
-
| `default` | Glance-era: UNIPI brand, model, thinking, directory, git \| context/tokens, tps, cost, clock |
|
|
55
|
-
| `classic` | The pre-2.12 balanced layout: model, api, tools, git \| tps, context, cost + compactor + memory + ralph |
|
|
56
|
-
| `minimal` | Essentials only: path, git, context |
|
|
57
|
-
| `compact` | Core + key stats: model, git, cost, context |
|
|
58
|
-
| `full` | Everything from all groups |
|
|
59
|
-
| `ascii` | Core segments with ASCII icons |
|
|
60
|
-
|
|
61
|
-
## Segment Groups
|
|
62
|
-
|
|
63
|
-
| Group | Default | Data Source |
|
|
64
|
-
|-------|---------|-------------|
|
|
65
|
-
| **core** | ON | Pi SDK (ctx.sessionManager, footerData) |
|
|
66
|
-
| **compactor** | ON | Live Pi session data; last-compaction event |
|
|
67
|
-
| **memory** | ON | `MEMORY_STORED`/`DELETED`/`CONSOLIDATED` events |
|
|
68
|
-
| **mcp** | ON | `MCP_SERVER_STARTED`/`STOPPED`/`ERROR` events |
|
|
69
|
-
| **ralph** | ON | `RALPH_LOOP_START`/`END`/`ITERATION_DONE` events |
|
|
70
|
-
| **workflow** | ON | `WORKFLOW_START`/`END` events |
|
|
71
|
-
| **kanboard** | ON | Kanboard registry (direct read) |
|
|
72
|
-
| **notify** | OFF | `NOTIFICATION_SENT` event |
|
|
73
|
-
| **status_ext** | ON | `footerData.getExtensionStatuses()` |
|
|
74
|
-
|
|
75
|
-
## Configurables
|
|
76
|
-
|
|
77
|
-
Settings in `~/.pi/agent/settings.json` under `unipi.footer`:
|
|
78
|
-
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"unipi": {
|
|
82
|
-
"footer": {
|
|
83
|
-
"enabled": true,
|
|
84
|
-
"preset": "default",
|
|
85
|
-
"glanceMode": true,
|
|
86
|
-
"separator": "powerline-thin",
|
|
87
|
-
"iconStyle": "nerd",
|
|
88
|
-
"colorMode": "auto",
|
|
89
|
-
"groups": {
|
|
90
|
-
"compactor": {
|
|
91
|
-
"show": true,
|
|
92
|
-
"segments": {
|
|
93
|
-
"session_events": true,
|
|
94
|
-
"compactions": true,
|
|
95
|
-
"tokens_saved": true
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
```
|
|
34
|
+
| Command | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `/unipi:footer` | Turns the footer on or off. |
|
|
37
|
+
| `/unipi:footer on` | Turns the footer on. |
|
|
38
|
+
| `/unipi:footer off` | Turns the footer off (plain pi editor). |
|
|
103
39
|
|
|
104
|
-
|
|
40
|
+
## The glance frame
|
|
105
41
|
|
|
106
|
-
|
|
107
|
-
|-------|------|
|
|
108
|
-
| `powerline` | Thick powerline arrows |
|
|
109
|
-
| `powerline-thin` | Thin powerline arrows (default) |
|
|
110
|
-
| `slash` | / |
|
|
111
|
-
| `pipe` | \| |
|
|
112
|
-
| `dot` | Middle dot |
|
|
113
|
-
| `ascii` | > < |
|
|
42
|
+
The frame has three parts:
|
|
114
43
|
|
|
115
|
-
|
|
44
|
+
- **Top border.** The UNIPI brand, the long-horizon mode, the git branch, and the plan and permission mode. The brand shows a moving rainbow. The frame also shows the rainbow when the thinking level is `xhigh` or `max` (unless **Rainbow** is `brand-only`).
|
|
45
|
+
- **Bottom border.** The workspace name, context use and window size, the model and the thinking level. With a [Fusion](../fusion/README.md) pair, it shows the lead and the sidekick. It also shows Kanboard claims.
|
|
46
|
+
- **Stats line.** The line below the input. A part stays hidden until it has data. For example, compactions show only after the first compaction.
|
|
116
47
|
|
|
117
|
-
|
|
118
|
-
|-------|-------------|
|
|
119
|
-
| `nerd` | Nerd Font glyphs (auto-detected) |
|
|
120
|
-
| `emoji` | Unicode symbols (works on most terminals) |
|
|
121
|
-
| `text` | Plain text labels (works everywhere). Glance frame drops the robot glyph: `UNIPI │ branch:main`, `workspace:unipi` |
|
|
48
|
+
The background-task line reads the [Background Tasks](../background-tasks/README.md) registry. It shows nothing when no task exists.
|
|
122
49
|
|
|
123
|
-
|
|
50
|
+
## Responsive behavior
|
|
124
51
|
|
|
125
|
-
|
|
52
|
+
- **Width.** Each stats-line part has a priority. When the line does not fit, the lowest-priority parts drop whole (never mid-part), in this order: compactions, cache, time, turns, speed, cost — tokens always survive. The frame titles degrade the same way: the Kanboard label, the Fusion pair, the plan/permission cluster and the mode label drop (lowest value first) before the branch or model ever truncate.
|
|
53
|
+
- **Width safety.** Nothing ever writes the last terminal column (a full-width line desyncs wrapping terminals).
|
|
54
|
+
- **Height.** Below 20 terminal rows the stats line and the background-task line hide; the frame stays.
|
|
126
55
|
|
|
127
|
-
|
|
128
|
-
|------|-------------|
|
|
129
|
-
| `auto` | Detect terminal support from environment (default) |
|
|
130
|
-
| `truecolor` | Force 24-bit ANSI color |
|
|
131
|
-
| `256` | Force xterm-256 color fallback |
|
|
132
|
-
| `none` | Disable footer color escapes |
|
|
56
|
+
## Settings
|
|
133
57
|
|
|
134
|
-
`
|
|
58
|
+
Open `/unipi:settings` → **Footer**. The file is `~/.unipi/config/footer/config.json`. A project file at `.unipi/config/footer/config.json` overrides it.
|
|
135
59
|
|
|
136
|
-
|
|
60
|
+
| Key | Default | What it does |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `enabled` | `true` | Turns the footer on or off. `false` leaves the plain pi editor. |
|
|
63
|
+
| `iconStyle` | `nerd` | Icon set: `nerd` (needs a Nerd Font), `emoji` or `text`. |
|
|
64
|
+
| `colorMode` | `auto` | `auto`, `truecolor`, `256` or `none`. The legacy value `mono` loads as `none`. |
|
|
65
|
+
| `rainbow` | `always` | `always` animates the brand (and the whole frame at `xhigh`/`max` thinking); `brand-only` never animates the whole frame; `off` disables the animation. |
|
|
66
|
+
| `processLine` | `true` | Shows the background-task line above the input. |
|
|
67
|
+
| `strip.turns` | `true` | Turn and step counters. |
|
|
68
|
+
| `strip.time` | `true` | Model time and tool time. |
|
|
69
|
+
| `strip.speed` | `true` | Average TTFT and tokens per second. |
|
|
70
|
+
| `strip.tokens` | `true` | Session input and output tokens. |
|
|
71
|
+
| `strip.cost` | `true` | Session cost, or `sub` when the model runs on a subscription. |
|
|
72
|
+
| `strip.compactions` | `true` | Compaction count, sizes and recency. |
|
|
73
|
+
| `strip.cache` | `true` | Cache hit percentage. |
|
|
74
|
+
| `badges.mode` | `true` | Long-horizon mode label beside the brand. |
|
|
75
|
+
| `badges.planPermission` | `true` | PLAN badge and permission mode in the top border. |
|
|
76
|
+
| `badges.fusion` | `true` | Fusion lead and sidekick in the bottom border. |
|
|
77
|
+
| `badges.kanboard` | `true` | Kanboard claims label in the top border. |
|
|
137
78
|
|
|
138
|
-
|
|
139
|
-
Wide terminal (>120 cols):
|
|
140
|
-
model | thinking | path | git | context | cost | compactions | tokens_saved | project_count
|
|
79
|
+
`colorMode: auto` uses 24-bit color where the terminal supports it, 256 colors in terminals such as Apple Terminal, and no color when the `NO_COLOR` environment variable exists.
|
|
141
80
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
81
|
+
Old v2 keys (`preset`, `separator`, `zoneSeparator`, `showFullLabels`, `groups`, `glanceMode`) in existing config files are ignored.
|
|
82
|
+
|
|
83
|
+
## How it works
|
|
84
|
+
|
|
85
|
+
The footer listens to UniPi events on the [event bus](../../docs/architecture/event-bus.md) for mode, plan and permission state. Usage data comes from an incremental scan of the session branch: each second only new entries are processed (a compaction or branch change triggers a full rescan), the same pass filling a cached snapshot for the stats line. The TPS tracker is fed live by pi's streaming events and reconciled by the same scan. The footer redraws when something it displays changed — plus once a second while the rainbow animates.
|
|
146
86
|
|
|
147
|
-
##
|
|
87
|
+
## See also
|
|
148
88
|
|
|
149
|
-
|
|
89
|
+
- [Info Screen](../info-screen/README.md)
|
|
90
|
+
- [Settings reference](../../docs/reference/settings.md)
|
package/index.ts
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/footer",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.30",
|
|
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": "3.0.0-alpha.
|
|
36
|
-
"@pi-unipi/background-tasks": "3.0.0-alpha.
|
|
35
|
+
"@pi-unipi/core": "3.0.0-alpha.30",
|
|
36
|
+
"@pi-unipi/background-tasks": "3.0.0-alpha.30"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@earendil-works/pi-coding-agent": "^0.87.1",
|
package/src/commands.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @pi-unipi/footer — Commands
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* /unipi:footer — toggle the footer on/off.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { UNIPI_PREFIX, FOOTER_COMMANDS } from "@pi-unipi/core";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import { applyGlanceMode, type FooterState } from "./index.js";
|
|
9
|
+
import { saveFooterSettings } from "./config.js";
|
|
10
|
+
import type { FooterState } from "./index.js";
|
|
11
|
+
import { fullRescan } from "./index.js";
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Register footer commands.
|
|
@@ -21,34 +20,17 @@ export function registerCommands(pi: ExtensionAPI, state: FooterState): void {
|
|
|
21
20
|
handler: async (args, ctx) => {
|
|
22
21
|
const arg = args?.trim().toLowerCase();
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
if (arg === "on")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
saveFooterSettings({ enabled: true });
|
|
29
|
-
state.setupUI?.(pi, ctx);
|
|
30
|
-
ctx.ui.notify("Footer enabled", "info");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
23
|
+
let enable: boolean;
|
|
24
|
+
if (arg === "on") enable = true;
|
|
25
|
+
else if (arg === "off") enable = false;
|
|
26
|
+
else enable = !state.enabled;
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
state.enabled = false;
|
|
37
|
-
state.renderer.setActive(false);
|
|
38
|
-
ctx.ui.setFooter(undefined);
|
|
39
|
-
ctx.ui.setWidget("footer-top", undefined);
|
|
40
|
-
ctx.ui.setWidget("footer-secondary", undefined);
|
|
41
|
-
saveFooterSettings({ enabled: false });
|
|
42
|
-
ctx.ui.notify("Footer disabled", "info");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Toggle (no args or unknown args)
|
|
47
|
-
state.enabled = !state.enabled;
|
|
48
|
-
state.renderer.setActive(state.enabled);
|
|
28
|
+
state.enabled = enable;
|
|
29
|
+
saveFooterSettings({ enabled: enable });
|
|
49
30
|
|
|
50
|
-
if (
|
|
31
|
+
if (enable) {
|
|
51
32
|
state.setupUI?.(pi, ctx);
|
|
33
|
+
fullRescan(state);
|
|
52
34
|
ctx.ui.notify("Footer enabled", "info");
|
|
53
35
|
} else {
|
|
54
36
|
ctx.ui.setFooter(undefined);
|
|
@@ -56,17 +38,6 @@ export function registerCommands(pi: ExtensionAPI, state: FooterState): void {
|
|
|
56
38
|
ctx.ui.setWidget("footer-secondary", undefined);
|
|
57
39
|
ctx.ui.notify("Footer disabled", "info");
|
|
58
40
|
}
|
|
59
|
-
|
|
60
|
-
saveFooterSettings({ enabled: state.enabled });
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// /unipi:footer-help — show help overlay
|
|
65
|
-
pi.registerCommand(`${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER_HELP}`, {
|
|
66
|
-
description: "Show footer segment guide (icons, labels, descriptions)",
|
|
67
|
-
handler: async (_args, _ctx) => {
|
|
68
|
-
const allSegments = Array.from(state.segmentLookup.values());
|
|
69
|
-
showFooterHelp(pi, allSegments, state.renderer.getPresetName());
|
|
70
41
|
},
|
|
71
42
|
});
|
|
72
43
|
}
|