pi-extensions-tool-display 1.0.1 → 1.1.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 +2 -0
- package/README.zh-CN.md +2 -0
- package/SKILL.md +28 -0
- package/package.json +5 -1
- package/src/config-modal.ts +9 -6
- package/src/index.ts +31 -12
- package/src/tool-overrides.ts +53 -8
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ It is also a standalone Pi extension. Install or include this package in Pi's pa
|
|
|
14
14
|
|
|
15
15
|
This package owns the display protocol and generic component composition only. Each feature extension continues to own its domain-specific audit extraction, copy, status, and layout.
|
|
16
16
|
|
|
17
|
+
Built-in overrides only replace tools that Pi has already activated; `registerToolOverrides` does not enable inactive built-in tools.
|
|
18
|
+
|
|
17
19
|
## Upstream attribution
|
|
18
20
|
|
|
19
21
|
The display integration is designed to work with the MIT-licensed [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display), the original full-featured Pi tool-display project. We thank MasuRii for that work.
|
package/README.zh-CN.md
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
这个包负责实际工具展示、展示协议和通用组件组合。具体业务的审计数据提取、文案、状态和布局仍由调用方维护,避免把不同扩展耦合在一起。
|
|
16
16
|
|
|
17
|
+
内建工具覆盖只替换 Pi 当前已经激活的工具;`registerToolOverrides` 不会启用原本未激活的内建工具。
|
|
18
|
+
|
|
17
19
|
## 上游引用
|
|
18
20
|
|
|
19
21
|
本包的展示集成设计用于兼容 MIT 许可的 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display),这是 Pi 生态中原始的完整工具展示项目。感谢 MasuRii 的工作。
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: configure-pi-extensions-tool-display
|
|
3
|
+
description: "配置与排查 Pi 工具展示宿主、内建/自定义工具 override、输出模式和 diff 视图。Use when changing pi-extensions-tool-display rendering."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 配置 pi-extensions-tool-display
|
|
7
|
+
|
|
8
|
+
## 诊断
|
|
9
|
+
|
|
10
|
+
读取实际 Pi agent 目录下的 `extensions/pi-tool-display/config.json`。注意目录名是 `pi-tool-display`,不是 npm 包名。以包内 `config.example.json` 和当前源码支持的字段为准。
|
|
11
|
+
|
|
12
|
+
确认没有同时加载另一份工具展示宿主;`pi-distill` 与 `pi-tool-supervisor` 已会加载这个共享宿主,通常无需重复安装。
|
|
13
|
+
|
|
14
|
+
## 修改
|
|
15
|
+
|
|
16
|
+
优先运行 `/config:tool-display`;`/tool-display`、`/pi-tool-display` 是兼容别名。按目标最小修改:
|
|
17
|
+
|
|
18
|
+
- `registerToolOverrides` 控制 `read/grep/find/ls/bash/edit/write` 的展示所有权;
|
|
19
|
+
- `customToolOverrides.<tool>` 使用 `enabled`、`kind`、`outputMode`;
|
|
20
|
+
- `readOutputMode`、`searchOutputMode`、`mcpOutputMode`、`bashOutputMode` 控制输出展示;
|
|
21
|
+
- `diffViewMode`、`diffIndicatorMode`、折叠行数和换行字段只影响 diff UI;
|
|
22
|
+
- `enabled: false` 关闭宿主展示。
|
|
23
|
+
|
|
24
|
+
不要把展示开关解释为禁用工具,也不要让多个宿主同时注册同一内建 override。
|
|
25
|
+
|
|
26
|
+
## 验证
|
|
27
|
+
|
|
28
|
+
分别调用一个受影响的内建或自定义工具,观察折叠、预览、diff 或输出模式。若消费者审计卡片未出现,先确认宿主只加载一次,再确认消费者 middleware 已注册;不能用配置文件可解析代替真实 UI 证据。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-extensions-tool-display",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Pi tool display host and shared result-rendering protocol for extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"config.example.json",
|
|
16
16
|
"README.md",
|
|
17
17
|
"README.zh-CN.md",
|
|
18
|
+
"SKILL.md",
|
|
18
19
|
"tsconfig.json"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
@@ -53,6 +54,9 @@
|
|
|
53
54
|
"pi": {
|
|
54
55
|
"extensions": [
|
|
55
56
|
"./index.ts"
|
|
57
|
+
],
|
|
58
|
+
"skills": [
|
|
59
|
+
"./SKILL.md"
|
|
56
60
|
]
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
package/src/config-modal.ts
CHANGED
|
@@ -104,7 +104,7 @@ export function buildInspectorSettings(
|
|
|
104
104
|
inspectorTitle: "Global Tool Display Switch",
|
|
105
105
|
inspectorSummary: [
|
|
106
106
|
"Enables or disables all pi-tool-display rendering and tool ownership behavior.",
|
|
107
|
-
"The /tool-display command remains available while disabled so the extension can be enabled again without editing Pi package settings.",
|
|
107
|
+
"The /config:tool-display command remains available while disabled so the extension can be enabled again without editing Pi package settings.",
|
|
108
108
|
],
|
|
109
109
|
inspectorOptions: [
|
|
110
110
|
"on — enable tool overrides and enhanced rendering",
|
|
@@ -500,7 +500,7 @@ export function handleToolDisplayArgs(args: string, ctx: ExtensionCommandContext
|
|
|
500
500
|
const candidate = normalized.slice("preset ".length).trim();
|
|
501
501
|
const preset = parseToolDisplayPreset(candidate);
|
|
502
502
|
if (!preset) {
|
|
503
|
-
ctx.ui.notify(`Unknown preset. Use: /tool-display preset ${PRESET_COMMAND_HINT}`, "warning");
|
|
503
|
+
ctx.ui.notify(`Unknown preset. Use: /config:tool-display preset ${PRESET_COMMAND_HINT}`, "warning");
|
|
504
504
|
return true;
|
|
505
505
|
}
|
|
506
506
|
|
|
@@ -509,7 +509,7 @@ export function handleToolDisplayArgs(args: string, ctx: ExtensionCommandContext
|
|
|
509
509
|
return true;
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
ctx.ui.notify(`Usage: /tool-display [show|reset|preset ${PRESET_COMMAND_HINT}]`, "warning");
|
|
512
|
+
ctx.ui.notify(`Usage: /config:tool-display [show|reset|preset ${PRESET_COMMAND_HINT}]`, "warning");
|
|
513
513
|
return true;
|
|
514
514
|
}
|
|
515
515
|
|
|
@@ -523,7 +523,7 @@ export async function runToolDisplayCommandHandler(
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
if (!ctx.hasUI) {
|
|
526
|
-
ctx.ui.notify("/tool-display requires interactive TUI mode.", "warning");
|
|
526
|
+
ctx.ui.notify("/config:tool-display requires interactive TUI mode.", "warning");
|
|
527
527
|
return;
|
|
528
528
|
}
|
|
529
529
|
|
|
@@ -531,10 +531,13 @@ export async function runToolDisplayCommandHandler(
|
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
export function registerToolDisplayCommand(pi: ExtensionAPI, controller: ToolDisplayConfigController): void {
|
|
534
|
-
|
|
534
|
+
const command = {
|
|
535
535
|
description: "Configure tool output rendering (OpenCode-style)",
|
|
536
536
|
handler: async (args, ctx) => {
|
|
537
537
|
await runToolDisplayCommandHandler(args, ctx, controller);
|
|
538
538
|
},
|
|
539
|
-
}
|
|
539
|
+
};
|
|
540
|
+
for (const name of ["config:tool-display", "tool-display", "pi-tool-display"] as const) {
|
|
541
|
+
pi.registerCommand(name, command);
|
|
542
|
+
}
|
|
540
543
|
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type ToolDisplayCapabilities,
|
|
14
14
|
} from "./capabilities.js";
|
|
15
15
|
import { registerToolDisplayOverrides } from "./tool-overrides.js";
|
|
16
|
+
import { logToolDisplayDebug } from "./debug-logger.js";
|
|
16
17
|
import { disposeAll, resetDisposed } from "./disposable.js";
|
|
17
18
|
import { registerThinkingLabeling } from "./thinking-label.js";
|
|
18
19
|
import registerNativeUserMessageBox from "./user-message-box-native.js";
|
|
@@ -24,6 +25,12 @@ import {
|
|
|
24
25
|
|
|
25
26
|
export * from "./result-render-middleware.js";
|
|
26
27
|
|
|
28
|
+
const TOOL_DISPLAY_RELOAD_ACTIVE_TOOLS_KEY = Symbol.for("pi-tool-display.reloadActiveTools.v1");
|
|
29
|
+
const BUILT_IN_TOOL_OVERRIDE_NAME_SET = new Set<string>(BUILT_IN_TOOL_OVERRIDE_NAMES);
|
|
30
|
+
type GlobalWithToolDisplayReloadState = typeof globalThis & {
|
|
31
|
+
[TOOL_DISPLAY_RELOAD_ACTIVE_TOOLS_KEY]?: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
27
34
|
function ownershipChanged(
|
|
28
35
|
previous: ToolDisplayConfig,
|
|
29
36
|
next: ToolDisplayConfig,
|
|
@@ -44,9 +51,9 @@ export function toolDisplayReloadRequired(
|
|
|
44
51
|
|
|
45
52
|
const initializedHosts = new WeakSet<ExtensionAPI>();
|
|
46
53
|
|
|
47
|
-
function hasRegisteredToolDisplayCommand(pi: ExtensionAPI): boolean {
|
|
54
|
+
function hasRegisteredToolDisplayCommand(pi: ExtensionAPI, name: string): boolean {
|
|
48
55
|
try {
|
|
49
|
-
return pi.getCommands().some((command) => command.name ===
|
|
56
|
+
return pi.getCommands().some((command) => command.name === name);
|
|
50
57
|
} catch {
|
|
51
58
|
return false;
|
|
52
59
|
}
|
|
@@ -65,10 +72,21 @@ export function ensureToolDisplayHost(
|
|
|
65
72
|
if (initializedHosts.has(pi)) return;
|
|
66
73
|
initializedHosts.add(pi);
|
|
67
74
|
|
|
75
|
+
const globalReloadState = globalThis as GlobalWithToolDisplayReloadState;
|
|
76
|
+
const activeBuiltInToolNamesBeforeReload = globalReloadState[TOOL_DISPLAY_RELOAD_ACTIVE_TOOLS_KEY];
|
|
77
|
+
delete globalReloadState[TOOL_DISPLAY_RELOAD_ACTIVE_TOOLS_KEY];
|
|
78
|
+
|
|
68
79
|
resetDisposed();
|
|
69
80
|
|
|
70
81
|
pi.on("session_shutdown", (event: { reason: string }) => {
|
|
71
82
|
if (event.reason === "reload") {
|
|
83
|
+
try {
|
|
84
|
+
globalReloadState[TOOL_DISPLAY_RELOAD_ACTIVE_TOOLS_KEY] = pi
|
|
85
|
+
.getActiveTools()
|
|
86
|
+
.filter((toolName) => BUILT_IN_TOOL_OVERRIDE_NAME_SET.has(toolName));
|
|
87
|
+
} catch (error) {
|
|
88
|
+
logToolDisplayDebug("Failed to preserve active built-in tools for reload.", error);
|
|
89
|
+
}
|
|
72
90
|
disposeAll();
|
|
73
91
|
initializedHosts.delete(pi);
|
|
74
92
|
}
|
|
@@ -112,22 +130,23 @@ export function ensureToolDisplayHost(
|
|
|
112
130
|
};
|
|
113
131
|
|
|
114
132
|
if (initial.config.enabled) {
|
|
115
|
-
registerToolDisplayOverrides(pi, getEffectiveConfig);
|
|
133
|
+
registerToolDisplayOverrides(pi, getEffectiveConfig, activeBuiltInToolNamesBeforeReload);
|
|
116
134
|
registerNativeUserMessageBox(pi, getConfig);
|
|
117
135
|
registerThinkingLabeling(pi);
|
|
118
136
|
}
|
|
119
137
|
|
|
120
138
|
// Each Pi extension has its own registration map, while getCommands() sees
|
|
121
139
|
// the shared runtime. Check the latter so feature extensions do not expose
|
|
122
|
-
// duplicate
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
140
|
+
// duplicate management commands when they initialize this host separately.
|
|
141
|
+
const command = {
|
|
142
|
+
description: "Configure tool output rendering (OpenCode-style)",
|
|
143
|
+
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
144
|
+
const { runToolDisplayCommandHandler } = await import("./config-modal.js");
|
|
145
|
+
await runToolDisplayCommandHandler(args, ctx, { getConfig, setConfig, getCapabilities });
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
for (const name of ["config:tool-display", "tool-display", "pi-tool-display"] as const) {
|
|
149
|
+
if (!hasRegisteredToolDisplayCommand(pi, name)) pi.registerCommand(name, command);
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
pi.on("session_start", async (_event, ctx) => {
|
package/src/tool-overrides.ts
CHANGED
|
@@ -1668,9 +1668,20 @@ function tryGetAllTools(pi: ExtensionAPI, debugMessage: string): unknown[] | und
|
|
|
1668
1668
|
}
|
|
1669
1669
|
}
|
|
1670
1670
|
|
|
1671
|
+
// Return undefined until Pi binds the extension action methods.
|
|
1672
|
+
function tryGetActiveToolNames(pi: ExtensionAPI, debugMessage: string): string[] | undefined {
|
|
1673
|
+
try {
|
|
1674
|
+
return pi.getActiveTools();
|
|
1675
|
+
} catch (error) {
|
|
1676
|
+
logToolDisplayDebug(debugMessage, error);
|
|
1677
|
+
return undefined;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1671
1681
|
export function registerToolDisplayOverrides(
|
|
1672
1682
|
pi: ExtensionAPI,
|
|
1673
1683
|
getConfig: ConfigGetter,
|
|
1684
|
+
activeToolNamesDuringLoad?: readonly string[],
|
|
1674
1685
|
): void {
|
|
1675
1686
|
clearBuiltInToolCache();
|
|
1676
1687
|
const toolDisplayApi = installToolDisplayApi(getConfig);
|
|
@@ -1688,7 +1699,7 @@ export function registerToolDisplayOverrides(
|
|
|
1688
1699
|
const registeredBuiltInToolOverrides = new Set<BuiltInToolOverrideName>();
|
|
1689
1700
|
|
|
1690
1701
|
const isExternallyOwnedBuiltInTool = (toolName: BuiltInToolOverrideName): boolean => {
|
|
1691
|
-
const allTools = tryGetAllTools(pi, "Built-in tool override ownership discovery unavailable
|
|
1702
|
+
const allTools = tryGetAllTools(pi, "Built-in tool override ownership discovery unavailable; skipping conflicting owners.");
|
|
1692
1703
|
if (!allTools) {
|
|
1693
1704
|
return false;
|
|
1694
1705
|
}
|
|
@@ -1780,7 +1791,37 @@ export function registerToolDisplayOverrides(
|
|
|
1780
1791
|
return { scope: getSearchScope(args), limitSuffix: args.limit !== undefined ? ` (limit ${args.limit})` : "" };
|
|
1781
1792
|
};
|
|
1782
1793
|
|
|
1783
|
-
|
|
1794
|
+
const builtInToolRegistrations = new Map<BuiltInToolOverrideName, () => void>();
|
|
1795
|
+
|
|
1796
|
+
// Record each built-in replacement until Pi exposes its active tool selection.
|
|
1797
|
+
const recordBuiltInToolRegistration = (
|
|
1798
|
+
toolName: BuiltInToolOverrideName,
|
|
1799
|
+
register: () => void,
|
|
1800
|
+
): void => {
|
|
1801
|
+
builtInToolRegistrations.set(toolName, register);
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1804
|
+
// Retry deferred replacements against Pi's current active tool selection.
|
|
1805
|
+
const registerActiveBuiltInToolOverrides = (
|
|
1806
|
+
knownActiveToolNames?: readonly string[],
|
|
1807
|
+
): void => {
|
|
1808
|
+
const activeToolNames = knownActiveToolNames ?? tryGetActiveToolNames(
|
|
1809
|
+
pi,
|
|
1810
|
+
"Built-in tool activation discovery unavailable; skipping display overrides.",
|
|
1811
|
+
);
|
|
1812
|
+
if (!activeToolNames) {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
const activeTools = new Set(activeToolNames);
|
|
1817
|
+
for (const [toolName, register] of builtInToolRegistrations) {
|
|
1818
|
+
if (activeTools.has(toolName)) {
|
|
1819
|
+
registerIfOwned(toolName, register);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
recordBuiltInToolRegistration("read", () => {
|
|
1784
1825
|
registerRuntimeTool(pi, {
|
|
1785
1826
|
name: "read",
|
|
1786
1827
|
label: "read",
|
|
@@ -1795,7 +1836,7 @@ export function registerToolDisplayOverrides(
|
|
|
1795
1836
|
});
|
|
1796
1837
|
});
|
|
1797
1838
|
|
|
1798
|
-
|
|
1839
|
+
recordBuiltInToolRegistration("grep", () => {
|
|
1799
1840
|
registerRuntimeTool(pi, {
|
|
1800
1841
|
name: "grep",
|
|
1801
1842
|
label: "grep",
|
|
@@ -1816,7 +1857,7 @@ export function registerToolDisplayOverrides(
|
|
|
1816
1857
|
});
|
|
1817
1858
|
});
|
|
1818
1859
|
|
|
1819
|
-
|
|
1860
|
+
recordBuiltInToolRegistration("find", () => {
|
|
1820
1861
|
registerRuntimeTool(pi, {
|
|
1821
1862
|
name: "find",
|
|
1822
1863
|
label: "find",
|
|
@@ -1834,7 +1875,7 @@ export function registerToolDisplayOverrides(
|
|
|
1834
1875
|
});
|
|
1835
1876
|
});
|
|
1836
1877
|
|
|
1837
|
-
|
|
1878
|
+
recordBuiltInToolRegistration("ls", () => {
|
|
1838
1879
|
registerRuntimeTool(pi, {
|
|
1839
1880
|
name: "ls",
|
|
1840
1881
|
label: "ls",
|
|
@@ -1850,7 +1891,7 @@ export function registerToolDisplayOverrides(
|
|
|
1850
1891
|
});
|
|
1851
1892
|
});
|
|
1852
1893
|
|
|
1853
|
-
|
|
1894
|
+
recordBuiltInToolRegistration("edit", () => {
|
|
1854
1895
|
registerRuntimeTool(pi, {
|
|
1855
1896
|
name: "edit",
|
|
1856
1897
|
label: "edit",
|
|
@@ -1872,7 +1913,7 @@ export function registerToolDisplayOverrides(
|
|
|
1872
1913
|
});
|
|
1873
1914
|
});
|
|
1874
1915
|
|
|
1875
|
-
|
|
1916
|
+
recordBuiltInToolRegistration("write", () => {
|
|
1876
1917
|
registerRuntimeTool(pi, {
|
|
1877
1918
|
name: "write",
|
|
1878
1919
|
label: "write",
|
|
@@ -1945,7 +1986,7 @@ export function registerToolDisplayOverrides(
|
|
|
1945
1986
|
});
|
|
1946
1987
|
});
|
|
1947
1988
|
|
|
1948
|
-
|
|
1989
|
+
recordBuiltInToolRegistration("bash", () => {
|
|
1949
1990
|
registerRuntimeTool(pi, {
|
|
1950
1991
|
name: "bash",
|
|
1951
1992
|
label: "bash",
|
|
@@ -2024,6 +2065,8 @@ export function registerToolDisplayOverrides(
|
|
|
2024
2065
|
});
|
|
2025
2066
|
});
|
|
2026
2067
|
|
|
2068
|
+
registerActiveBuiltInToolOverrides(activeToolNamesDuringLoad);
|
|
2069
|
+
|
|
2027
2070
|
const wrappedCustomToolNames = new Set<string>();
|
|
2028
2071
|
registerCleanup(() => wrappedCustomToolNames.clear());
|
|
2029
2072
|
|
|
@@ -2229,11 +2272,13 @@ export function registerToolDisplayOverrides(
|
|
|
2229
2272
|
|
|
2230
2273
|
pi.on("session_start", async () => {
|
|
2231
2274
|
clearWriteExecutionMeta(writeExecutionMetaByToolCallId);
|
|
2275
|
+
registerActiveBuiltInToolOverrides();
|
|
2232
2276
|
registerMcpToolOverrides();
|
|
2233
2277
|
scheduleMcpToolOverrideDiscovery();
|
|
2234
2278
|
});
|
|
2235
2279
|
pi.on("before_agent_start", async () => {
|
|
2236
2280
|
clearWriteExecutionMeta(writeExecutionMetaByToolCallId);
|
|
2281
|
+
registerActiveBuiltInToolOverrides();
|
|
2237
2282
|
registerMcpToolOverrides();
|
|
2238
2283
|
scheduleMcpToolOverrideDiscovery();
|
|
2239
2284
|
});
|