From 6dc6cf41c4e3c4b94940497f5aa7d467fcf43b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D0=BE=D1=80=D0=BE=D0=B4=D0=B8=D0=BD=20=D0=A0=D0=BE?= =?UTF-8?q?=D0=BC=D0=B0=D0=BD?= Date: Sat, 26 Sep 2026 11:53:51 +0300 Subject: [PATCH] feat: expand execute tool into child tool spoilers when script has tool calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Если execute скрипт содержит tools..(...) — показываем отдельные спойлеры для каждого найденного инструмента - Если нет вызовов инструментов — показываем execute как обычно - Args для дочерних инструментов извлекаются из кода regex --- index.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/index.ts b/index.ts index 66c8143..f7e3d3d 100644 --- a/index.ts +++ b/index.ts @@ -367,14 +367,36 @@ async function runPlugin(ctx: any, options: any, dataDir: string) { // Tool calls if (step.tools.length > 0) { for (const tool of step.tools) { - parts.push(`
\n🔧 ${tool.name}\n\n`) - parts.push(`Args:\n\`\`\`\n${tool.args}\n\`\`\`\n\n`) - if (tool.failed) { - parts.push(`Failed: ${tool.result}\n\n`) + // Check if this is an execute tool with child tool calls + // Child tools have toolID like "parentID/toolName" + const isExecuteWithChildren = tool.name === "execute" && tool.toolID.includes("/") + const childTools = isExecuteWithChildren ? step.tools.filter(t => t.toolID.startsWith(tool.toolID + "/")) : [] + + if (isExecuteWithChildren && childTools.length > 0) { + // Expand execute → individual spoilers for each child tool + for (const child of childTools) { + // Extract args from the code for this child tool + const childArgs = extractChildToolArgs(tool.args, child.name) + parts.push(`
\n🔧 ${child.name}\n\n`) + parts.push(`Args:\n\`\`\`\n${childArgs}\n\`\`\`\n\n`) + if (child.failed) { + parts.push(`Failed: ${child.result}\n\n`) + } else if (child.result) { + parts.push(`Result:\n\`\`\`\n${child.result}\n\`\`\`\n\n`) + } + parts.push(`
`) + } } else { - parts.push(`Result:\n\`\`\`\n${tool.result}\n\`\`\`\n\n`) + // Normal tool (or execute without children) + parts.push(`
\n🔧 ${tool.name}\n\n`) + parts.push(`Args:\n\`\`\`\n${tool.args}\n\`\`\`\n\n`) + if (tool.failed) { + parts.push(`Failed: ${tool.result}\n\n`) + } else { + parts.push(`Result:\n\`\`\`\n${tool.result}\n\`\`\`\n\n`) + } + parts.push(`
`) } - parts.push(`
`) } } @@ -387,6 +409,28 @@ async function runPlugin(ctx: any, options: any, dataDir: string) { return parts.join("\n") } + // Extract args for a child tool from the execute code + function extractChildToolArgs(code: string, toolName: string): string { + try { + const args = JSON.parse(code) + const codeStr = args?.code || "" + // Match tools..(...) and extract the arguments + const regex = new RegExp(`tools\\.\\w+\\.${escapeRegex(toolName.replace("_", "."))}\\s*\\(([^)]*)\\)`, "g") + const matches: string[] = [] + let match + while ((match = regex.exec(codeStr)) !== null) { + matches.push(match[1].trim()) + } + return matches.join("\n") || "(parsed from code)" + } catch { + return "(parsed from code)" + } + } + + function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + } + // ============================================================================= // Send message to Matrix (reuses existing sendReply/sendNotice) // =============================================================================