"use strict"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Type } from "typebox"; import { getConfig } from "../config"; import { processImage } from "../media"; import { visionCompletion } from "../client"; import { ERROR_DIAGNOSIS } from "../prompts"; import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "@earendil-works/pi-coding-agent"; export function registerDiagnoseErrorTool(pi: ExtensionAPI) { pi.registerTool({ name: "diagnose_error_screenshot", label: "Diagnose Error", description: "Analyze error screenshots (stack traces, error dialogs, console errors) to identify root cause, explain why the error occurred, and provide step-by-step fix guidance with code examples. Supports local files (png, jpg) and HTTP(S) URLs.", promptSnippet: "Analyze error screenshots — identify root cause and suggest fixes with code", promptGuidelines: [ "Use diagnose_error_screenshot when the user shares an error screenshot and wants to understand what went wrong and how to fix it. Provide an optional context string (e.g., 'Running npm install on Windows') to improve diagnosis accuracy.", ], parameters: Type.Object({ imagePath: Type.String({ description: "Path to the error screenshot (relative to cwd, supports @ prefix) or HTTP(S) URL.", }), prompt: Type.Optional( Type.String({ description: "Additional context about the error or specific questions to answer. Defaults to 'Analyze this error and suggest fixes.'", }) ), context: Type.Optional( Type.String({ description: "What the user was doing when the error occurred (e.g., 'Running cargo build', 'Starting the dev server').", }) ), }), async execute(_toolCallId, params, signal, onUpdate, ctx) { const config = getConfig(); onUpdate?.({ content: [{ type: "text", text: "正在调用 GLM-4.6V 诊断错误..." }], }); const image = processImage(params.imagePath, ctx.cwd, config.maxImageSizeMB); // 构建 user prompt let userPrompt = params.prompt || "Analyze this error and suggest fixes."; if (params.context) { userPrompt = `Context: ${params.context}\n\n${userPrompt}`; } const result = await visionCompletion( config, ERROR_DIAGNOSIS, [image], userPrompt, signal ); const truncated = truncateHead(result, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES, }); let text = truncated.content; if (truncated.truncated) { text += `\n\n[输出已截断: ${truncated.outputLines}/${truncated.totalLines} 行]`; } return { content: [{ type: "text", text }], details: { raw: result }, }; }, }); }