- Added SkillMessageComponent to render /skill command messages as compact skill entries with expandable/collapsible UI showing skill metadata and optional full prompt content. - Added promptCustomMessage() public method to AgentSession to support custom message types with metadata and details properties. - Added SKILL_PROMPT_MESSAGE_TYPE constant and SkillPromptDetails interface to support skill-specific message handling. - Enhanced patch tool documentation with guidance on structured block editing, error recovery, and best practices for maintaining code integrity. - Refactored message handling in AgentSession by extracting _promptWithMessage() private method to consolidate common logic between prompt() and promptCustomMessage().
93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import type { TextContent } from "@oh-my-pi/pi-ai";
|
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
import { Box, Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
import type { CustomMessage, SkillPromptDetails } from "../../session/messages";
|
|
|
|
export class SkillMessageComponent extends Container {
|
|
private message: CustomMessage<SkillPromptDetails>;
|
|
private box: Box;
|
|
private contentComponent?: Component;
|
|
private _expanded = false;
|
|
|
|
constructor(message: CustomMessage<SkillPromptDetails>) {
|
|
super();
|
|
this.message = message;
|
|
this.addChild(new Spacer(1));
|
|
|
|
this.box = new Box(1, 1, t => theme.bg("customMessageBg", t));
|
|
this.rebuild();
|
|
}
|
|
|
|
setExpanded(expanded: boolean): void {
|
|
if (this._expanded !== expanded) {
|
|
this._expanded = expanded;
|
|
this.rebuild();
|
|
}
|
|
}
|
|
|
|
override invalidate(): void {
|
|
super.invalidate();
|
|
this.rebuild();
|
|
}
|
|
|
|
private rebuild(): void {
|
|
if (this.contentComponent) {
|
|
this.removeChild(this.contentComponent);
|
|
this.contentComponent = undefined;
|
|
}
|
|
|
|
this.removeChild(this.box);
|
|
this.addChild(this.box);
|
|
this.box.clear();
|
|
|
|
const label = theme.fg("customMessageLabel", theme.bold("[skill]"));
|
|
this.box.addChild(new Text(label, 0, 0));
|
|
this.box.addChild(new Spacer(1));
|
|
|
|
const details = this.message.details;
|
|
const args = details?.args?.trim();
|
|
const infoLines = [
|
|
`Skill: ${details?.name ?? "unknown"}`,
|
|
args ? `Args: ${args}` : undefined,
|
|
details?.path ? `Path: ${details.path}` : undefined,
|
|
typeof details?.lineCount === "number" ? `Prompt: ${details.lineCount} lines` : undefined,
|
|
].filter((line): line is string => Boolean(line));
|
|
|
|
this.box.addChild(
|
|
new Markdown(infoLines.join("\n"), 0, 0, getMarkdownTheme(), {
|
|
color: (value: string) => theme.fg("customMessageText", value),
|
|
}),
|
|
);
|
|
|
|
if (!this._expanded) {
|
|
return;
|
|
}
|
|
|
|
const text = this.extractText();
|
|
if (!text) {
|
|
return;
|
|
}
|
|
|
|
this.box.addChild(new Spacer(1));
|
|
const promptHeader = theme.fg("customMessageLabel", theme.bold("Prompt"));
|
|
this.box.addChild(new Text(promptHeader, 0, 0));
|
|
this.box.addChild(new Spacer(1));
|
|
|
|
this.contentComponent = new Markdown(text, 0, 0, getMarkdownTheme(), {
|
|
color: (value: string) => theme.fg("customMessageText", value),
|
|
});
|
|
this.box.addChild(this.contentComponent);
|
|
}
|
|
|
|
private extractText(): string {
|
|
if (typeof this.message.content === "string") {
|
|
return this.message.content;
|
|
}
|
|
return this.message.content
|
|
.filter((c): c is TextContent => c.type === "text")
|
|
.map(c => c.text)
|
|
.join("\n");
|
|
}
|
|
}
|