diff --git a/index.ts b/index.ts index c36bb32..03db2b0 100644 --- a/index.ts +++ b/index.ts @@ -11,7 +11,7 @@ import { parseFrontmatter, withFileMutationQueue, } from "@mariozechner/pi-coding-agent"; -import { Text } from "@mariozechner/pi-tui"; +import { Markdown, Text, type MarkdownTheme } from "@mariozechner/pi-tui"; import { Type } from "typebox"; interface PromptConfig { @@ -62,6 +62,27 @@ const PROMPTS_DIR = path.join(EXTENSION_DIR, "prompts"); const FINALIZE_TOOL_NAME = "subagent_finalize"; const MAX_FINALIZE_RETRIES = 2; +// Markdown Theme - Render finalized subagent text with Pi's terminal markdown component. +function getSubagentMarkdownTheme(theme: Theme): MarkdownTheme { + return { + heading: (text) => theme.fg("mdHeading", text), + link: (text) => theme.fg("mdLink", text), + linkUrl: (text) => theme.fg("mdLinkUrl", text), + code: (text) => theme.fg("mdCode", text), + codeBlock: (text) => theme.fg("mdCodeBlock", text), + codeBlockBorder: (text) => theme.fg("mdCodeBlockBorder", text), + quote: (text) => theme.fg("mdQuote", text), + quoteBorder: (text) => theme.fg("mdQuoteBorder", text), + hr: (text) => theme.fg("mdHr", text), + listBullet: (text) => theme.fg("mdListBullet", text), + bold: (text) => theme.bold(text), + italic: (text) => theme.italic(text), + strikethrough: (text) => theme.strikethrough(text), + underline: (text) => theme.underline(text), + codeBlockIndent: " ", + }; +} + // Format Tool Content - Some clients hide structured details from the model. function formatSubagentContent( status: "SUCCESS" | "ERROR", @@ -73,7 +94,9 @@ function formatSubagentContent( if (error?.trim()) header.push(`**Error:** ${error.trim()}`); const body = result?.trimEnd(); - return body ? `${header.join(" \n")}\n\n---\n\n${body}` : header.join(" \n"); + return body + ? `${header.join(" \n")}\n\n---\n\n${body}` + : header.join(" \n"); } // Parse Tool List - Frontmatter may use YAML arrays or comma-delimited strings. @@ -180,7 +203,9 @@ function getSubagentSessionPath( } // Validate Finalize Payload - Keep the parent contract strict and small. -function validateFinalizePayload(value: unknown): SubagentFinalizePayload | null { +function validateFinalizePayload( + value: unknown, +): SubagentFinalizePayload | null { if (!value || typeof value !== "object") return null; const payload = value as Record; if (payload.status === "SUCCESS") { @@ -437,7 +462,8 @@ async function runAgent( ); const toolName = String(event.toolName ?? "tool"); if (toolName === FINALIZE_TOOL_NAME) { - result.finalized = validateFinalizePayload(event.args) ?? undefined; + result.finalized = + validateFinalizePayload(event.args) ?? undefined; } activeToolIds.add(id); result.status.state = "running"; @@ -647,7 +673,11 @@ export default function (pi: ExtensionAPI) { await fs.promises.mkdir(path.dirname(sessionPath), { recursive: true }); let result: SubagentResult | null = null; - for (let retryCount = 0; retryCount <= MAX_FINALIZE_RETRIES; retryCount += 1) { + for ( + let retryCount = 0; + retryCount <= MAX_FINALIZE_RETRIES; + retryCount += 1 + ) { const task = retryCount === 0 ? `Task: ${params.task}` @@ -700,7 +730,12 @@ export default function (pi: ExtensionAPI) { content: [ { type: "text", - text: formatSubagentContent("ERROR", sessionId, undefined, fallback), + text: formatSubagentContent( + "ERROR", + sessionId, + undefined, + fallback, + ), }, ], details: { sessionId }, @@ -760,7 +795,12 @@ export default function (pi: ExtensionAPI) { } const text = result.content[0]; - return new Text(text?.type === "text" ? text.text : "", 0, 0); + return new Markdown( + text?.type === "text" ? text.text : "", + 0, + 0, + getSubagentMarkdownTheme(theme), + ); }, }); }