rinco-pi-hud 1.3.0 → 1.3.1
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 +1 -1
- package/docs/CONTRIBUTING.md +2 -1
- package/docs/README.zh-CN.md +1 -1
- package/docs/footer.md +5 -3
- package/extensions/hud/commands/settings.ts +2 -2
- package/extensions/hud/config/config-counts.ts +3 -0
- package/extensions/hud/footer/index.ts +2 -11
- package/extensions/hud/footer/render-labels.ts +4 -5
- package/extensions/hud/segments/mcp-status.ts +26 -21
- package/extensions/hud/state/index.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ See [docs/footer.md](docs/footer.md) for the full variable and segment reference
|
|
|
113
113
|
| [`extensions/hud/ui/`](extensions/hud/ui/) | Icons and terminal style utilities |
|
|
114
114
|
| [`docs/footer.md`](docs/footer.md) | Complete footer configuration reference |
|
|
115
115
|
| [`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md) | Local development, testing guidance, and pull request checklist |
|
|
116
|
-
| [`tests/`](tests/) | Vitest suite: footer layout, telemetry, provider balances, MCP
|
|
116
|
+
| [`tests/`](tests/) | Vitest suite: footer layout, telemetry, provider balances, MCP configuration counts |
|
|
117
117
|
|
|
118
118
|
## Development
|
|
119
119
|
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -50,7 +50,8 @@ npm run pack:check
|
|
|
50
50
|
|
|
51
51
|
Tests live in `tests/`:
|
|
52
52
|
|
|
53
|
-
- `zentui-status.test.ts` — Config counts, MCP
|
|
53
|
+
- `zentui-status.test.ts` — Config counts, MCP configuration counts, and extension status helpers.
|
|
54
|
+
- `mcp-footer.test.ts` — MCP count rendering, segment controls, custom formats, and legacy third-party statuses.
|
|
54
55
|
- `codex-usage.test.ts` — Codex payload normalization and model-specific quota formatting.
|
|
55
56
|
- `footer-layout.test.ts` — Four-group categorized layout, segment wrapping, and narrow-width budget.
|
|
56
57
|
- `footer-groups.test.ts` — Semantic Project, Session, Activity, and Usage segment ordering.
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -113,7 +113,7 @@ Usage 行跟随当前模型的 Provider:
|
|
|
113
113
|
| [`extensions/hud/ui/`](../extensions/hud/ui/) | 图标与终端样式工具 |
|
|
114
114
|
| [`docs/footer.md`](footer.md) | 完整脚注配置参考 |
|
|
115
115
|
| [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) | 本地开发、测试规范和 PR 检查清单 |
|
|
116
|
-
| [`tests/`](../tests/) | Vitest 测试套件:脚注布局、遥测、各家余额与 MCP
|
|
116
|
+
| [`tests/`](../tests/) | Vitest 测试套件:脚注布局、遥测、各家余额与 MCP 配置计数 |
|
|
117
117
|
|
|
118
118
|
## 开发
|
|
119
119
|
|
package/docs/footer.md
CHANGED
|
@@ -7,10 +7,12 @@ When `footerFormat` is empty, the HUD displays four semantic groups:
|
|
|
7
7
|
```text
|
|
8
8
|
Project · pi-sakura · main · 24.18
|
|
9
9
|
Session · gpt-5.3 · Thinking level: high · Turn 8
|
|
10
|
-
Activity · Agent idle · Skill 2/3 · MCP 2
|
|
10
|
+
Activity · Agent idle · Skill 2/3 · MCP 2
|
|
11
11
|
Usage · 35%/200k · ↑ 4.2k · ↓ 1.1k · $ 0.030
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
The MCP segment counts server entries in Pi's agent-directory `mcp.json` and the project's `.pi/mcp.json`. Same-name entries count once; disabled entries are included. Zero entries hide the segment. Counts refresh with the project state polling interval. This does not report connection state, validate individual server settings, or include servers registered dynamically by extensions. Missing, unreadable, malformed, or larger-than-1-MiB files contribute no entries. Legacy adapter footer statuses remain ordinary third-party statuses.
|
|
15
|
+
|
|
14
16
|
The category column is sized from visible text so every separator aligns. When a row exceeds the terminal width, complete status segments move to an indented continuation line. Only a single segment that is wider than the available content area is truncated. Third-party extension statuses appear on the Activity group in configured `left`, `middle`, then `right` order.
|
|
15
17
|
|
|
16
18
|
Control each default-layout row independently:
|
|
@@ -50,7 +52,7 @@ The command shape is `/zentui row <project|session|activity|usage> <enable|disab
|
|
|
50
52
|
| `activeAgents` | on | Activity | Active primary agent runs |
|
|
51
53
|
| `agentIdle` | on | Activity | `Agent idle` when no primary agent run is active |
|
|
52
54
|
| `skills` | on | Activity | Active/available skills count, e.g. `Skill 1/3` |
|
|
53
|
-
| `mcp` | on | Activity | MCP
|
|
55
|
+
| `mcp` | on | Activity | File-configured MCP servers, e.g. `MCP 2` |
|
|
54
56
|
| `context` | on | Usage | Context percentage, window size, and/or gauge |
|
|
55
57
|
| `inputTokens` | on | Usage | Cumulative input tokens |
|
|
56
58
|
| `outputTokens` | on | Usage | Cumulative output tokens |
|
|
@@ -119,7 +121,7 @@ Clear it:
|
|
|
119
121
|
| `$skills` | Active/available skills, e.g. `Skill 1/3` |
|
|
120
122
|
| `$active_skills` | Active skills count only |
|
|
121
123
|
| `$extensions` | Installed Pi packages count |
|
|
122
|
-
| `$mcp` | MCP
|
|
124
|
+
| `$mcp` | File-configured MCP server count |
|
|
123
125
|
| `$tool_counts` | Native tool completion counts |
|
|
124
126
|
| `$running_tools` | Recently running tools |
|
|
125
127
|
| `$active_agents` | Active primary agent runs |
|
|
@@ -133,7 +133,7 @@ const footerSegmentSettingLabels: Record<FooterSegmentSettingId, string> = {
|
|
|
133
133
|
activeAgents: "Active agents",
|
|
134
134
|
agentIdle: "Agent idle status",
|
|
135
135
|
skills: "Skill activity",
|
|
136
|
-
mcp: "MCP
|
|
136
|
+
mcp: "MCP configured servers",
|
|
137
137
|
context: "Context usage",
|
|
138
138
|
inputTokens: "Input tokens",
|
|
139
139
|
outputTokens: "Output tokens",
|
|
@@ -173,7 +173,7 @@ const footerSegmentSettingDescriptions: Record<FooterSegmentSettingId, string> =
|
|
|
173
173
|
codexUsage: "Show OpenAI Codex subscription rate limits on the usage row when available.",
|
|
174
174
|
configCounts: "Show instruction-file and installed Pi package counts on the project row.",
|
|
175
175
|
skills: "Show active/available Pi skill counts on the activity row.",
|
|
176
|
-
mcp: "Show
|
|
176
|
+
mcp: "Show the configured server count from global and project mcp.json, including disabled entries.",
|
|
177
177
|
runningTools: "Show currently running native tools and their elapsed time.",
|
|
178
178
|
toolCounts: "Show cumulative completed native-tool counts.",
|
|
179
179
|
activeAgents: "Show active main agent runs on the activity row (not subagent count).",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { isAbsolute, join } from "node:path";
|
|
4
|
+
import { countConfiguredMcpServers } from "../segments/mcp-status.js";
|
|
4
5
|
|
|
5
6
|
export interface ConfigCounts {
|
|
6
7
|
instructionFiles: {
|
|
@@ -9,6 +10,7 @@ export interface ConfigCounts {
|
|
|
9
10
|
total: number;
|
|
10
11
|
};
|
|
11
12
|
packages: number;
|
|
13
|
+
mcpServers: number;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface ConfigCountOptions {
|
|
@@ -82,6 +84,7 @@ export function countConfigEntries(cwd: string, options: ConfigCountOptions = {}
|
|
|
82
84
|
total: agentsMd + claudeMd,
|
|
83
85
|
},
|
|
84
86
|
packages: countPackages(agentDir),
|
|
87
|
+
mcpServers: countConfiguredMcpServers(cwd, agentDir),
|
|
85
88
|
};
|
|
86
89
|
}
|
|
87
90
|
|
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
} from "../telemetry/format.js";
|
|
32
32
|
import { resolveRuntimeSymbol } from "../ui/icons.js";
|
|
33
33
|
import type { LiveContextOverride } from "../session/live-context.js";
|
|
34
|
-
import { parseMcpStatus } from "../segments/mcp-status.js";
|
|
35
34
|
import type { FooterState } from "../state/index.js";
|
|
36
35
|
import { renderStyleForSource } from "../ui/style.js";
|
|
37
36
|
|
|
@@ -74,7 +73,6 @@ export function installFooter(
|
|
|
74
73
|
const colorSource = config.colorSources.starship;
|
|
75
74
|
const iconMode = config.icons.mode;
|
|
76
75
|
const rawExtensionStatuses = footerData.getExtensionStatuses();
|
|
77
|
-
const mcpStatus = parseMcpStatus(rawExtensionStatuses.get("mcp"));
|
|
78
76
|
const { safeCwd, safeRuntime, safePackageVersion, safeCommit } = sanitizeFooterInputs(
|
|
79
77
|
ctx,
|
|
80
78
|
state,
|
|
@@ -166,7 +164,7 @@ export function installFooter(
|
|
|
166
164
|
runningToolsLabel,
|
|
167
165
|
activeAgentsLabel,
|
|
168
166
|
agentIdleLabel,
|
|
169
|
-
} = buildTelemetryLabels(ctx, state, config, theme, colorSource
|
|
167
|
+
} = buildTelemetryLabels(ctx, state, config, theme, colorSource);
|
|
170
168
|
const renderVariable = (name: string): string => {
|
|
171
169
|
const canonical = FOOTER_FORMAT_ALIASES[name] ?? name;
|
|
172
170
|
switch (canonical) {
|
|
@@ -439,14 +437,7 @@ export function installFooter(
|
|
|
439
437
|
formatTimeLabel(""),
|
|
440
438
|
)}`
|
|
441
439
|
: "";
|
|
442
|
-
const
|
|
443
|
-
const dedicatedMcpVisible = Boolean(
|
|
444
|
-
mcpStatus && (config.footerFormat ? formatNeedsMcp : config.footerSegments.mcp),
|
|
445
|
-
);
|
|
446
|
-
const extensionStatusSource = dedicatedMcpVisible
|
|
447
|
-
? new Map([...rawExtensionStatuses].filter(([key]) => key !== "mcp"))
|
|
448
|
-
: rawExtensionStatuses;
|
|
449
|
-
const extensionStatuses = collectExtensionStatusSegments(extensionStatusSource, config);
|
|
440
|
+
const extensionStatuses = collectExtensionStatusSegments(rawExtensionStatuses, config);
|
|
450
441
|
const renderExtensionStatus = (segment: ExtensionStatusSegment) =>
|
|
451
442
|
segment.colorMode === "original"
|
|
452
443
|
? segment.text
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
12
12
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
13
13
|
import type { ColorSource, PolishedTuiConfig } from "../config/config.js";
|
|
14
|
-
import type { McpStatus } from "../segments/mcp-status.js";
|
|
15
14
|
import { formatSkillCounts } from "../segments/skill-activity.js";
|
|
16
15
|
import type { FooterState } from "../state/index.js";
|
|
17
16
|
import { getTelemetryStats } from "../state/telemetry.js";
|
|
@@ -164,7 +163,6 @@ export function buildTelemetryLabels(
|
|
|
164
163
|
config: PolishedTuiConfig,
|
|
165
164
|
theme: ThemeLike,
|
|
166
165
|
colorSource: ColorSource,
|
|
167
|
-
mcpStatus: McpStatus | undefined,
|
|
168
166
|
): TelemetryLabels {
|
|
169
167
|
const statusStyle = (style: string, text: string) =>
|
|
170
168
|
text ? renderStyleForSource(theme, colorSource, style, text) : "";
|
|
@@ -267,9 +265,10 @@ export function buildTelemetryLabels(
|
|
|
267
265
|
: "";
|
|
268
266
|
const configCountsLabel = [instructionFilesLabel, extensionsLabel].filter(Boolean).join(" ");
|
|
269
267
|
|
|
270
|
-
const mcpLabel =
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
const mcpLabel =
|
|
269
|
+
state.configCounts.mcpServers > 0
|
|
270
|
+
? statusStyle(config.colors.gitStatus, `MCP ${state.configCounts.mcpServers}`)
|
|
271
|
+
: "";
|
|
273
272
|
|
|
274
273
|
const telemetryStats = getTelemetryStats(state.telemetry);
|
|
275
274
|
const toolCountsText = Object.entries(telemetryStats.completedToolCounts)
|
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
const MAX_CONFIG_BYTES = 1024 * 1024;
|
|
5
|
+
|
|
6
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (![connected, total, disabled].every(Number.isSafeInteger)) return undefined;
|
|
21
|
-
if (connected < 0 || total < 0 || connected > total || total > 10_000 || disabled > 10_000)
|
|
22
|
-
return undefined;
|
|
23
|
-
return { connected, total };
|
|
10
|
+
function readServerNames(path: string): string[] {
|
|
11
|
+
try {
|
|
12
|
+
const stats = statSync(path);
|
|
13
|
+
if (!stats.isFile() || stats.size > MAX_CONFIG_BYTES) return [];
|
|
14
|
+
const config: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
15
|
+
if (!isRecord(config) || !isRecord(config.mcpServers)) return [];
|
|
16
|
+
return Object.entries(config.mcpServers)
|
|
17
|
+
.filter(([name, entry]) => /^[A-Za-z0-9_-]+$/.test(name) && isRecord(entry))
|
|
18
|
+
.map(([name]) => name);
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
/** Count file-configured server entries, including disabled ones, without resolving secrets
|
|
25
|
+
* or starting connections. Project entries override global entries with the same name.
|
|
26
|
+
* This is not a connection count or a validation of individual server settings. */
|
|
27
|
+
export function countConfiguredMcpServers(cwd: string, agentDir: string): number {
|
|
28
|
+
const globalNames = agentDir ? readServerNames(join(agentDir, "mcp.json")) : [];
|
|
29
|
+
const projectNames = readServerNames(join(cwd, ".pi", "mcp.json"));
|
|
30
|
+
return new Set([...globalNames, ...projectNames]).size;
|
|
31
|
+
}
|