@oh-my-pi/pi-tui 18.4.6 → 18.4.9
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 +30 -0
- package/dist/types/apps/debug/log-viewer.d.ts +17 -0
- package/dist/types/apps/debug/raw-sse.d.ts +14 -0
- package/dist/types/native/backend.d.ts +11 -2
- package/dist/types/native/encode.d.ts +5 -1
- package/dist/types/native/node.d.ts +14 -1
- package/dist/types/native/reconcile.d.ts +5 -0
- package/dist/types/overlays/extensions/extension-dashboard.d.ts +31 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +37 -0
- package/dist/types/overlays/extensions/inspector-panel.d.ts +8 -0
- package/dist/types/overlays/jobs-panel.d.ts +35 -9
- package/dist/types/prompt/word-completion.d.ts +12 -0
- package/dist/types/terminal.d.ts +7 -0
- package/dist/types/theme/defaults/index.d.ts +700 -0
- package/dist/types/theme/schema.d.ts +18 -0
- package/dist/types/tools/bash-interactive.d.ts +15 -0
- package/dist/types/tools/output-meta.d.ts +8 -2
- package/dist/types/tools/streaming-output.d.ts +15 -10
- package/dist/types/tui.d.ts +5 -0
- package/dist/types/windows-input-mode.d.ts +26 -0
- package/package.json +9 -9
- package/src/apps/debug/log-viewer.ts +236 -2
- package/src/apps/debug/protocol-probe.ts +10 -1
- package/src/apps/debug/raw-sse.ts +210 -28
- package/src/native/apply.ts +2 -1
- package/src/native/backend.ts +66 -7
- package/src/native/encode.ts +7 -2
- package/src/native/node.ts +15 -1
- package/src/native/reconcile.ts +28 -1
- package/src/overlays/extensions/extension-dashboard.ts +275 -23
- package/src/overlays/extensions/extension-list.ts +274 -14
- package/src/overlays/extensions/inspector-panel.ts +368 -0
- package/src/overlays/hook-selector.ts +1 -1
- package/src/overlays/jobs-panel.ts +222 -49
- package/src/prompt/composer-cache.ts +26 -3
- package/src/prompt/word-completion.ts +31 -13
- package/src/terminal.ts +37 -1
- package/src/theme/dark.json +10 -0
- package/src/theme/defaults/alabaster.json +10 -0
- package/src/theme/defaults/amethyst.json +10 -0
- package/src/theme/defaults/anthracite.json +10 -0
- package/src/theme/defaults/basalt.json +10 -0
- package/src/theme/defaults/birch.json +10 -0
- package/src/theme/defaults/dark-abyss.json +10 -0
- package/src/theme/defaults/dark-arctic.json +10 -0
- package/src/theme/defaults/dark-aurora.json +10 -0
- package/src/theme/defaults/dark-catppuccin.json +10 -0
- package/src/theme/defaults/dark-cavern.json +10 -0
- package/src/theme/defaults/dark-celestial.json +10 -0
- package/src/theme/defaults/dark-copper.json +10 -0
- package/src/theme/defaults/dark-cosmos.json +10 -0
- package/src/theme/defaults/dark-cyberpunk.json +10 -0
- package/src/theme/defaults/dark-dracula.json +10 -0
- package/src/theme/defaults/dark-eclipse.json +10 -0
- package/src/theme/defaults/dark-ember.json +10 -0
- package/src/theme/defaults/dark-equinox.json +10 -0
- package/src/theme/defaults/dark-forest.json +10 -0
- package/src/theme/defaults/dark-github.json +10 -0
- package/src/theme/defaults/dark-gruvbox.json +10 -0
- package/src/theme/defaults/dark-lavender.json +10 -0
- package/src/theme/defaults/dark-lunar.json +10 -0
- package/src/theme/defaults/dark-midnight.json +10 -0
- package/src/theme/defaults/dark-monochrome.json +10 -0
- package/src/theme/defaults/dark-monokai.json +10 -0
- package/src/theme/defaults/dark-nebula.json +10 -0
- package/src/theme/defaults/dark-neon-noir.json +10 -0
- package/src/theme/defaults/dark-nord.json +10 -0
- package/src/theme/defaults/dark-ocean.json +10 -0
- package/src/theme/defaults/dark-one.json +10 -0
- package/src/theme/defaults/dark-poimandres.json +10 -0
- package/src/theme/defaults/dark-rainforest.json +10 -0
- package/src/theme/defaults/dark-reef.json +10 -0
- package/src/theme/defaults/dark-retro.json +10 -0
- package/src/theme/defaults/dark-rose-pine.json +10 -0
- package/src/theme/defaults/dark-sakura.json +10 -0
- package/src/theme/defaults/dark-slate.json +10 -0
- package/src/theme/defaults/dark-solarized.json +10 -0
- package/src/theme/defaults/dark-solstice.json +10 -0
- package/src/theme/defaults/dark-starfall.json +10 -0
- package/src/theme/defaults/dark-sunset.json +10 -0
- package/src/theme/defaults/dark-swamp.json +10 -0
- package/src/theme/defaults/dark-synthwave.json +10 -0
- package/src/theme/defaults/dark-taiga.json +10 -0
- package/src/theme/defaults/dark-terminal.json +10 -0
- package/src/theme/defaults/dark-tokyo-night.json +10 -0
- package/src/theme/defaults/dark-tundra.json +10 -0
- package/src/theme/defaults/dark-twilight.json +10 -0
- package/src/theme/defaults/dark-volcanic.json +10 -0
- package/src/theme/defaults/graphite.json +10 -0
- package/src/theme/defaults/light-arctic.json +10 -0
- package/src/theme/defaults/light-aurora-day.json +10 -0
- package/src/theme/defaults/light-canyon.json +10 -0
- package/src/theme/defaults/light-catppuccin.json +10 -0
- package/src/theme/defaults/light-cirrus.json +10 -0
- package/src/theme/defaults/light-coral.json +10 -0
- package/src/theme/defaults/light-cyberpunk.json +10 -0
- package/src/theme/defaults/light-dawn.json +10 -0
- package/src/theme/defaults/light-dunes.json +10 -0
- package/src/theme/defaults/light-eucalyptus.json +10 -0
- package/src/theme/defaults/light-forest.json +10 -0
- package/src/theme/defaults/light-frost.json +10 -0
- package/src/theme/defaults/light-github.json +10 -0
- package/src/theme/defaults/light-glacier.json +10 -0
- package/src/theme/defaults/light-gruvbox.json +10 -0
- package/src/theme/defaults/light-haze.json +10 -0
- package/src/theme/defaults/light-honeycomb.json +10 -0
- package/src/theme/defaults/light-lagoon.json +10 -0
- package/src/theme/defaults/light-lavender.json +10 -0
- package/src/theme/defaults/light-meadow.json +10 -0
- package/src/theme/defaults/light-mint.json +10 -0
- package/src/theme/defaults/light-monochrome.json +10 -0
- package/src/theme/defaults/light-ocean.json +10 -0
- package/src/theme/defaults/light-one.json +10 -0
- package/src/theme/defaults/light-opal.json +10 -0
- package/src/theme/defaults/light-orchard.json +10 -0
- package/src/theme/defaults/light-paper.json +10 -0
- package/src/theme/defaults/light-poimandres.json +10 -0
- package/src/theme/defaults/light-prism.json +10 -0
- package/src/theme/defaults/light-retro.json +10 -0
- package/src/theme/defaults/light-sand.json +10 -0
- package/src/theme/defaults/light-savanna.json +10 -0
- package/src/theme/defaults/light-solarized.json +10 -0
- package/src/theme/defaults/light-soleil.json +10 -0
- package/src/theme/defaults/light-sunset.json +10 -0
- package/src/theme/defaults/light-synthwave.json +10 -0
- package/src/theme/defaults/light-tokyo-night.json +10 -0
- package/src/theme/defaults/light-wetland.json +10 -0
- package/src/theme/defaults/light-zenith.json +10 -0
- package/src/theme/defaults/limestone.json +10 -0
- package/src/theme/defaults/mahogany.json +10 -0
- package/src/theme/defaults/marble.json +10 -0
- package/src/theme/defaults/obsidian.json +10 -0
- package/src/theme/defaults/onyx.json +10 -0
- package/src/theme/defaults/pearl.json +10 -0
- package/src/theme/defaults/porcelain.json +10 -0
- package/src/theme/defaults/quartz.json +10 -0
- package/src/theme/defaults/sandstone.json +10 -0
- package/src/theme/defaults/titanium.json +10 -0
- package/src/theme/light.json +10 -0
- package/src/theme/schema-validation.ts +7 -0
- package/src/theme/schema.ts +18 -0
- package/src/theme/theme-schema.json +30 -0
- package/src/tools/bash-interactive.ts +85 -16
- package/src/tools/output-meta.ts +13 -5
- package/src/tools/streaming-output.ts +48 -23
- package/src/tui.ts +56 -15
- package/src/windows-input-mode.ts +206 -0
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* kind-specific surface → contents → boring config.
|
|
7
7
|
*/
|
|
8
8
|
import * as os from "node:os";
|
|
9
|
+
import type { TspSpan, TspText } from "@oh-my-pi/pi-wire";
|
|
9
10
|
import type { Component } from "../../tui";
|
|
11
|
+
import { code, col, compact, keyed, kv, node, span, stableKey, text } from "../../native/describe";
|
|
12
|
+
import type { NativeChild, NativeNode } from "../../native/node";
|
|
10
13
|
import { visibleWidth, wrapTextWithAnsi } from "../../utils";
|
|
11
14
|
import { KeyValueList } from "../../components/key-value-list";
|
|
12
15
|
import { Section } from "../../components/section";
|
|
@@ -41,6 +44,7 @@ import {
|
|
|
41
44
|
formatMcpHealthLabel,
|
|
42
45
|
isDiscoveredMcpServer,
|
|
43
46
|
type MCPConnectionHealth,
|
|
47
|
+
type MCPRuntimeCatalogItem,
|
|
44
48
|
type MCPRuntimeSource,
|
|
45
49
|
snapshotMcpRuntime,
|
|
46
50
|
visibleMcpTools,
|
|
@@ -62,6 +66,27 @@ interface KindView {
|
|
|
62
66
|
config: string[];
|
|
63
67
|
}
|
|
64
68
|
|
|
69
|
+
/** {@link KindView} as native nodes: the same grammar, semantic instead of pre-styled lines. */
|
|
70
|
+
interface NativeKindView {
|
|
71
|
+
title?: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
guidance?: string;
|
|
74
|
+
/** Runtime facts ahead of the enablement status (MCP health, server, website, skill visibility). */
|
|
75
|
+
runtime: [string, TspText | undefined][];
|
|
76
|
+
/** The runtime facts carry a health line: the status shows only when not active (the ANSI `runtimeLine`). */
|
|
77
|
+
health?: boolean;
|
|
78
|
+
sections: NativeNode[];
|
|
79
|
+
preview?: { heading: string; text: string };
|
|
80
|
+
config: [string, TspText | undefined][];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const MCP_HEALTH_STYLE: Record<MCPConnectionHealth, string> = {
|
|
84
|
+
connected: "success",
|
|
85
|
+
connecting: "muted",
|
|
86
|
+
disconnected: "dim",
|
|
87
|
+
inactive: "warning",
|
|
88
|
+
};
|
|
89
|
+
|
|
65
90
|
const PREVIEW_LINE_BUDGET = PREVIEW_LIMITS.EXPANDED_LINES;
|
|
66
91
|
const MCP_TOOL_BUDGET = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
67
92
|
const MCP_INLINE_ARG_LIMIT = 3;
|
|
@@ -124,6 +149,341 @@ export class InspectorPanel implements Component {
|
|
|
124
149
|
return this.#renderExtension(this.#extension, width);
|
|
125
150
|
}
|
|
126
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The selected extension as native preview nodes, in the same order as
|
|
154
|
+
* {@link render}: identity, runtime/enablement and origin facts,
|
|
155
|
+
* description, guidance, kind sections, content preview, config. The
|
|
156
|
+
* collapsed budgets follow {@link toggleExpanded} like the ANSI view.
|
|
157
|
+
*/
|
|
158
|
+
describePreview(): NativeNode[] {
|
|
159
|
+
const ext = this.#extension;
|
|
160
|
+
if (!ext) {
|
|
161
|
+
return [text([span("Select an extension to view details", "muted")], { wrap: "word" })];
|
|
162
|
+
}
|
|
163
|
+
this.#toolFrame = snapshotToolRuntimeSource(this.#toolSource);
|
|
164
|
+
const view = this.#nativeKindView(ext);
|
|
165
|
+
const name = sanitizeDisplayLine(ext.displayName);
|
|
166
|
+
const title = sanitizeDisplayLineField(view.title);
|
|
167
|
+
const levelLabel = ext.source.level === "user" ? "User" : ext.source.level === "project" ? "Project" : "Native";
|
|
168
|
+
const filePath = sanitizeDisplayText(ext.path);
|
|
169
|
+
const facts = kv([
|
|
170
|
+
...view.runtime,
|
|
171
|
+
["Status", view.health && ext.state === "active" ? undefined : this.#nativeStatus(ext)],
|
|
172
|
+
["Origin", `via ${sanitizeDisplayLine(ext.source.providerName)} (${levelLabel})`],
|
|
173
|
+
[
|
|
174
|
+
"Path",
|
|
175
|
+
[
|
|
176
|
+
span(
|
|
177
|
+
sanitizeDisplayText(shortenPath(ext.path, os.homedir())),
|
|
178
|
+
"path dim",
|
|
179
|
+
filePath.startsWith("/") ? { href: `file://${filePath}` } : undefined,
|
|
180
|
+
),
|
|
181
|
+
],
|
|
182
|
+
],
|
|
183
|
+
]);
|
|
184
|
+
const out: NativeNode[] = compact<NativeNode>([
|
|
185
|
+
node("text", { text: name, role: "omp.picker.title" }, undefined, "title"),
|
|
186
|
+
title && title !== name ? node("text", { spans: [span(title, "muted")] }, undefined, "subtitle") : undefined,
|
|
187
|
+
facts && keyed(facts, "facts"),
|
|
188
|
+
this.#nativeShortText(view.description, "description"),
|
|
189
|
+
this.#nativeShortText(view.guidance, "guidance"),
|
|
190
|
+
...view.sections,
|
|
191
|
+
]);
|
|
192
|
+
if (view.preview) out.push(this.#nativePreviewSection(view.preview.heading, view.preview.text));
|
|
193
|
+
const config = kv(view.config);
|
|
194
|
+
if (config) out.push(keyed(config, "config"));
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#nativeStatus(ext: Extension): TspSpan[] {
|
|
199
|
+
switch (ext.state) {
|
|
200
|
+
case "active":
|
|
201
|
+
return [span(`${theme.status.enabled} ${enablementLabel(ext.state)}`, "success")];
|
|
202
|
+
case "disabled":
|
|
203
|
+
return [span(`${theme.status.disabled} ${enablementLabel(ext.state, ext.disabledReason)}`, "dim")];
|
|
204
|
+
case "shadowed":
|
|
205
|
+
return [
|
|
206
|
+
span(
|
|
207
|
+
`${theme.status.shadowed} ${enablementLabel(ext.state, ext.disabledReason, ext.shadowedBy)}`,
|
|
208
|
+
"warning",
|
|
209
|
+
),
|
|
210
|
+
];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Description/guidance prose, clamped to the inline budget until expanded. */
|
|
215
|
+
#nativeShortText(value: string | undefined, key: string): NativeNode | undefined {
|
|
216
|
+
const cleaned = sanitizeDisplayField(value);
|
|
217
|
+
if (!cleaned) return undefined;
|
|
218
|
+
return node(
|
|
219
|
+
"text",
|
|
220
|
+
{
|
|
221
|
+
text: sanitizeDisplayText(cleaned),
|
|
222
|
+
wrap: "word",
|
|
223
|
+
lines: this.#expanded ? undefined : MCP_INLINE_DESC_LINES,
|
|
224
|
+
},
|
|
225
|
+
undefined,
|
|
226
|
+
key,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
#nativeSection(head: string, children: readonly NativeChild[]): NativeNode {
|
|
231
|
+
return node("section", { head: [span(head, "muted")] }, children, stableKey(`section:${head}`));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** File content as markdown source, cut to the preview budget until expanded. */
|
|
235
|
+
#nativePreviewSection(heading: string, raw: string): NativeNode {
|
|
236
|
+
if (!raw) return this.#nativeSection(heading, [text([span("(empty)", "dim")])]);
|
|
237
|
+
const lines = sanitizeDisplayText(raw).split("\n");
|
|
238
|
+
const cut = !this.#expanded && lines.length > PREVIEW_LINE_BUDGET;
|
|
239
|
+
const shown = cut ? lines.slice(0, PREVIEW_LINE_BUDGET - 1) : lines;
|
|
240
|
+
const children: NativeChild[] = [code(shown.join("\n"), { lang: "md", wrap: true })];
|
|
241
|
+
if (cut) children.push(this.#nativeMore(lines.length - shown.length, "lines"));
|
|
242
|
+
return this.#nativeSection(heading, children);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
#nativeMore(hidden: number, noun: string): NativeNode {
|
|
246
|
+
return text([span(`… ${hidden} more ${noun} (${expandKeyHint()} to expand)`, "dim")]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Tool/argument rows: name, type and required flag, then the description. */
|
|
250
|
+
#nativeParams(params: ToolParamView[]): NativeNode {
|
|
251
|
+
if (params.length === 0) return text([span("(no arguments)", "dim")]);
|
|
252
|
+
return node("kv", {
|
|
253
|
+
items: params.map(param => ({
|
|
254
|
+
k: [span(param.name, "accent mono")],
|
|
255
|
+
v: compact<TspSpan>([
|
|
256
|
+
span(param.type, "muted"),
|
|
257
|
+
span(` ${param.flag}`, param.required ? "warning" : "dim"),
|
|
258
|
+
param.description ? span(` ${sanitizeDisplayLine(param.description)}`) : undefined,
|
|
259
|
+
]),
|
|
260
|
+
})),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** One catalog entry (MCP tool or factory tool): name, title, description, arguments. */
|
|
265
|
+
#nativeCatalogEntry(
|
|
266
|
+
name: string,
|
|
267
|
+
title: string | undefined,
|
|
268
|
+
description: string | undefined,
|
|
269
|
+
parameters: unknown,
|
|
270
|
+
): NativeNode {
|
|
271
|
+
const params = toolParamsFromSchema(parameters);
|
|
272
|
+
const inline = this.#expanded || params.length <= MCP_INLINE_ARG_LIMIT;
|
|
273
|
+
return col(
|
|
274
|
+
compact<NativeChild>([
|
|
275
|
+
text(
|
|
276
|
+
compact<TspSpan>([
|
|
277
|
+
span(sanitizeDisplayLine(name), "accent mono"),
|
|
278
|
+
title && title !== name ? span(` ${sanitizeDisplayLine(title)}`, "muted") : undefined,
|
|
279
|
+
]),
|
|
280
|
+
),
|
|
281
|
+
description ? text(sanitizeDisplayText(description), { wrap: "word" }) : undefined,
|
|
282
|
+
inline
|
|
283
|
+
? this.#nativeParams(params)
|
|
284
|
+
: text([span(`${params.length} args (${expandKeyHint()} to expand)`, "dim")]),
|
|
285
|
+
]),
|
|
286
|
+
{ gap: "xs" },
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** A name-only catalog (MCP resources/prompts) under its budget until expanded. */
|
|
291
|
+
#nativeNameList(head: string, entries: MCPRuntimeCatalogItem[]): NativeNode {
|
|
292
|
+
const { shown, hidden } = visibleMcpTools(entries, this.#expanded ? entries.length : MCP_TOOL_BUDGET);
|
|
293
|
+
const children: NativeChild[] = shown.map(entry => text([span(entry.name, "accent mono")]));
|
|
294
|
+
if (hidden > 0) children.push(this.#nativeMore(hidden, ""));
|
|
295
|
+
return this.#nativeSection(head, children);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#nativeKindView(ext: Extension): NativeKindView {
|
|
299
|
+
const base: NativeKindView = { runtime: [], sections: [], config: [] };
|
|
300
|
+
switch (ext.kind) {
|
|
301
|
+
case "mcp":
|
|
302
|
+
return this.#nativeMcpKind(ext);
|
|
303
|
+
case "tool": {
|
|
304
|
+
const lives = liveToolsForExtension(ext, this.#toolFrame);
|
|
305
|
+
const data = toolInspectorData(ext, lives, this.#source);
|
|
306
|
+
if (data.factory.length > 1) {
|
|
307
|
+
return {
|
|
308
|
+
...base,
|
|
309
|
+
description: data.description,
|
|
310
|
+
sections: [
|
|
311
|
+
this.#nativeSection(
|
|
312
|
+
"Tools",
|
|
313
|
+
data.factory.map(tool =>
|
|
314
|
+
this.#nativeCatalogEntry(tool.name, tool.label, tool.description, tool.parameters),
|
|
315
|
+
),
|
|
316
|
+
),
|
|
317
|
+
],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
if (lives.length === 0 && data.params.length === 0) return { ...base, description: data.description };
|
|
321
|
+
return {
|
|
322
|
+
...base,
|
|
323
|
+
title: data.label,
|
|
324
|
+
description: data.description,
|
|
325
|
+
sections: [this.#nativeSection("Arguments", [this.#nativeParams(data.params)])],
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
case "rule": {
|
|
329
|
+
const data = ruleInspectorData(ext, this.#source);
|
|
330
|
+
const applies = kv([
|
|
331
|
+
["always", data.alwaysApply ? [span("always", "accent")] : undefined],
|
|
332
|
+
["globs", data.globs?.join(", ")],
|
|
333
|
+
["condition", this.#nativeList(data.condition)],
|
|
334
|
+
["ast", this.#nativeList(data.astCondition)],
|
|
335
|
+
["scope", this.#nativeList(data.scope)],
|
|
336
|
+
["agents", this.#nativeList(data.agents)],
|
|
337
|
+
["interrupt", data.interruptMode ? [span(data.interruptMode, "dim")] : undefined],
|
|
338
|
+
]);
|
|
339
|
+
return {
|
|
340
|
+
...base,
|
|
341
|
+
description: data.description,
|
|
342
|
+
sections: [this.#nativeSection("Applies", [applies ?? text([span("(no apply conditions)", "dim")])])],
|
|
343
|
+
preview: { heading: "Rule", text: data.content },
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
case "skill": {
|
|
347
|
+
const data = skillInspectorData(ext);
|
|
348
|
+
const surface = kv([
|
|
349
|
+
["apply", data.alwaysApply ? [span("always apply", "accent")] : undefined],
|
|
350
|
+
["globs", data.globs?.join(", ")],
|
|
351
|
+
]);
|
|
352
|
+
return {
|
|
353
|
+
...base,
|
|
354
|
+
description: data.description,
|
|
355
|
+
runtime: data.hidden
|
|
356
|
+
? [["Visibility", [span("hidden", "warning"), span(" omitted from the system-prompt skill list")]]]
|
|
357
|
+
: [],
|
|
358
|
+
sections: surface ? [keyed(surface, "surface")] : [],
|
|
359
|
+
preview: { heading: "Instruction", text: data.content },
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
case "slash-command": {
|
|
363
|
+
const data = commandInspectorData(ext);
|
|
364
|
+
const invocation: NativeChild[] = [text([span(`/${sanitizeDisplayText(ext.name)}`, "accent mono")])];
|
|
365
|
+
const facts = kv([
|
|
366
|
+
["hint", data.argumentHint ? [span(sanitizeDisplayText(data.argumentHint), "dim")] : undefined],
|
|
367
|
+
]);
|
|
368
|
+
if (facts) invocation.push(facts);
|
|
369
|
+
if (data.usesArguments) invocation.push(text([span("accepts $ARGUMENTS", "dim")]));
|
|
370
|
+
return {
|
|
371
|
+
...base,
|
|
372
|
+
description: data.description,
|
|
373
|
+
sections: [this.#nativeSection("Invocation", invocation)],
|
|
374
|
+
preview: { heading: "Template", text: data.body },
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
case "hook": {
|
|
378
|
+
const data = hookInspectorData(ext);
|
|
379
|
+
const facts = kv([
|
|
380
|
+
["when", data.hookType],
|
|
381
|
+
["tool", data.tool],
|
|
382
|
+
]);
|
|
383
|
+
return {
|
|
384
|
+
...base,
|
|
385
|
+
description: ext.description,
|
|
386
|
+
sections: [this.#nativeSection("Hook", facts ? [facts] : [])],
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
case "prompt":
|
|
390
|
+
return {
|
|
391
|
+
...base,
|
|
392
|
+
description: ext.description,
|
|
393
|
+
preview: { heading: "Prompt", text: promptInspectorData(ext).content },
|
|
394
|
+
};
|
|
395
|
+
case "context-file":
|
|
396
|
+
return {
|
|
397
|
+
...base,
|
|
398
|
+
description: ext.description,
|
|
399
|
+
preview: { heading: "Preview", text: contextInspectorData(ext).content },
|
|
400
|
+
};
|
|
401
|
+
case "instruction": {
|
|
402
|
+
const data = instructionInspectorData(ext);
|
|
403
|
+
const files = data.applyTo ? kv([["files", data.applyTo]]) : undefined;
|
|
404
|
+
return {
|
|
405
|
+
...base,
|
|
406
|
+
description: ext.description,
|
|
407
|
+
sections: files ? [this.#nativeSection("Applies", [files])] : [],
|
|
408
|
+
preview: { heading: "Instruction", text: data.content },
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
default:
|
|
412
|
+
return {
|
|
413
|
+
...base,
|
|
414
|
+
description: ext.description,
|
|
415
|
+
sections: ext.trigger
|
|
416
|
+
? [this.#nativeSection("Trigger", [text([span(sanitizeDisplayText(ext.trigger), "accent mono")])])]
|
|
417
|
+
: [],
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** A pattern list for a `kv` value, capped until expanded. */
|
|
423
|
+
#nativeList(items: string[] | undefined): TspSpan[] | undefined {
|
|
424
|
+
if (!items || items.length === 0) return undefined;
|
|
425
|
+
const cap = this.#expanded ? items.length : PREVIEW_LIMITS.COLLAPSED_LINES;
|
|
426
|
+
const shown = items.slice(0, cap).map(item => sanitizeDisplayText(item));
|
|
427
|
+
const hidden = items.length - shown.length;
|
|
428
|
+
const spans: TspSpan[] = [span(shown.join("\n"), "accent")];
|
|
429
|
+
if (hidden > 0) spans.push(span(`\n… ${hidden} more (${expandKeyHint()} to expand)`, "dim"));
|
|
430
|
+
return spans;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
#nativeMcpKind(ext: Extension): NativeKindView {
|
|
434
|
+
const shadowed = isShadowedExtension(ext);
|
|
435
|
+
if (shadowed) {
|
|
436
|
+
const command =
|
|
437
|
+
isDiscoveredMcpServer(ext.raw) && ext.raw.command
|
|
438
|
+
? shortenPath(sanitizeDisplayText(ext.raw.command), os.homedir())
|
|
439
|
+
: undefined;
|
|
440
|
+
return { runtime: [], sections: [], config: [["Command", command ? [span(command, "mono")] : undefined]] };
|
|
441
|
+
}
|
|
442
|
+
const snap = isDiscoveredMcpServer(ext.raw)
|
|
443
|
+
? snapshotMcpRuntime(ext.raw, this.#mcpSource, { enabled: ext.state !== "disabled" })
|
|
444
|
+
: undefined;
|
|
445
|
+
const health: MCPConnectionHealth = snap?.health ?? "disconnected";
|
|
446
|
+
const runtime: [string, TspText | undefined][] = [
|
|
447
|
+
[
|
|
448
|
+
"Health",
|
|
449
|
+
[
|
|
450
|
+
span(`${MCP_HEALTH_GLYPH[health]()} ${formatMcpHealthLabel(health)}`, MCP_HEALTH_STYLE[health]),
|
|
451
|
+
span(` ${snap?.transport ?? "stdio"}`, "muted"),
|
|
452
|
+
],
|
|
453
|
+
],
|
|
454
|
+
];
|
|
455
|
+
if (snap?.implementationName) {
|
|
456
|
+
const version = snap.implementationVersion ? ` ${snap.implementationVersion}` : "";
|
|
457
|
+
runtime.push(["Server", [span(`${snap.implementationName}${version}`, "dim")]]);
|
|
458
|
+
if (snap.websiteUrl) runtime.push(["Website", [span(snap.websiteUrl, "dim", { href: snap.websiteUrl })]]);
|
|
459
|
+
}
|
|
460
|
+
const sections: NativeNode[] = [];
|
|
461
|
+
if (snap && snap.tools.length > 0) {
|
|
462
|
+
const { shown, hidden } = visibleMcpTools(snap.tools, this.#expanded ? snap.tools.length : MCP_TOOL_BUDGET);
|
|
463
|
+
const entries: NativeChild[] = shown.map(tool =>
|
|
464
|
+
this.#nativeCatalogEntry(tool.name, tool.title, tool.description, tool.parameters),
|
|
465
|
+
);
|
|
466
|
+
if (hidden > 0) entries.push(this.#nativeMore(hidden, "tools"));
|
|
467
|
+
sections.push(this.#nativeSection("Tools", entries));
|
|
468
|
+
}
|
|
469
|
+
if (snap && snap.resources.length > 0) sections.push(this.#nativeNameList("Resources", snap.resources));
|
|
470
|
+
if (snap && snap.prompts.length > 0) sections.push(this.#nativeNameList("Prompts", snap.prompts));
|
|
471
|
+
return {
|
|
472
|
+
title: snap?.title,
|
|
473
|
+
description: snap?.description,
|
|
474
|
+
guidance: snap?.instructions,
|
|
475
|
+
runtime,
|
|
476
|
+
health: true,
|
|
477
|
+
sections,
|
|
478
|
+
config: [
|
|
479
|
+
["Command", snap?.command ? [span(shortenPath(snap.command, os.homedir()), "mono")] : undefined],
|
|
480
|
+
["URL", snap?.url ? [span(snap.url, "mono", { href: snap.url })] : undefined],
|
|
481
|
+
["Args", snap?.args && snap.args.length > 0 ? [span(snap.args.join(" "), "mono dim")] : undefined],
|
|
482
|
+
["Env vars", snap && snap.envCount > 0 ? [span(`${snap.envCount} defined`, "dim")] : undefined],
|
|
483
|
+
],
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
|
|
127
487
|
#renderExtension(ext: Extension, width: number): string[] {
|
|
128
488
|
const lines: string[] = [];
|
|
129
489
|
const kind = this.#kindView(ext);
|
|
@@ -663,6 +1023,14 @@ export class InspectorPanel implements Component {
|
|
|
663
1023
|
}
|
|
664
1024
|
}
|
|
665
1025
|
|
|
1026
|
+
/** Glyph of each MCP health, read at call time so glyph presets apply. */
|
|
1027
|
+
const MCP_HEALTH_GLYPH: Record<MCPConnectionHealth, () => string> = {
|
|
1028
|
+
connected: () => theme.status.enabled,
|
|
1029
|
+
connecting: () => theme.status.running,
|
|
1030
|
+
disconnected: () => theme.status.shadowed,
|
|
1031
|
+
inactive: () => theme.status.disabled,
|
|
1032
|
+
};
|
|
1033
|
+
|
|
666
1034
|
function inspectorExtensionKey(extension: Extension | null): string | null {
|
|
667
1035
|
if (!extension) return null;
|
|
668
1036
|
return `${extension.kind}:${extension.id}:${extension.path}`;
|
|
@@ -707,7 +707,7 @@ export class HookSelectorComponent extends OverlayPanel {
|
|
|
707
707
|
const checkbox = this.#selectionMarker === "checkbox";
|
|
708
708
|
const result = dockedPicker({
|
|
709
709
|
title: fixed.title,
|
|
710
|
-
...(this.#detailLines.length > 0 ? { subtitle: plainText(this.#detailLines.join("
|
|
710
|
+
...(this.#detailLines.length > 0 ? { subtitle: plainText(this.#detailLines.join("\n")) } : {}),
|
|
711
711
|
noun: "options",
|
|
712
712
|
size: "md",
|
|
713
713
|
layout: "rows",
|