From fc6ddbd1b2c6c77367cffb2fc73eab3267310e0a Mon Sep 17 00:00:00 2001 From: Horus AI Date: Tue, 24 Mar 2026 21:59:53 +0100 Subject: [PATCH] feat: Enhanced analyze - shows overview, scripts, sections, better project matching --- .clawhub/lock.json | 2 +- app/api/clawhub/route.ts | 78 +++++++++++++++++++++++----- app/mission-control/clawhub/page.tsx | 40 +++++++++++--- data/eod_briefs.json | 12 +++++ 4 files changed, 113 insertions(+), 19 deletions(-) diff --git a/.clawhub/lock.json b/.clawhub/lock.json index 519b856..5ee699e 100644 --- a/.clawhub/lock.json +++ b/.clawhub/lock.json @@ -19,7 +19,7 @@ }, "agentic-workflow-automation": { "version": "0.1.0", - "installedAt": 1774375021538 + "installedAt": 1774385419847 } } } diff --git a/app/api/clawhub/route.ts b/app/api/clawhub/route.ts index 519dd8c..b0f2764 100644 --- a/app/api/clawhub/route.ts +++ b/app/api/clawhub/route.ts @@ -132,6 +132,7 @@ export async function POST(req: NextRequest) { try { await execAsync(`rm -rf ${tempPath} 2>/dev/null; ${CLAWHUB_BIN} install ${slug} --dir ${tempPath} 2>&1`); + // Find SKILL.md const skillMdPath = path.join(tempPath, slug, "SKILL.md"); let content = ""; if (existsSync(skillMdPath)) { @@ -141,43 +142,96 @@ export async function POST(req: NextRequest) { if (existsSync(altPath)) content = await readFile(altPath, "utf-8"); } + // Extract description from frontmatter const descMatch = content.match(/description:\s*([^\n]+)/i); const desc = descMatch ? descMatch[1].trim() : "No description available"; + // Extract triggers const triggerMatch = content.match(/triggers?:\s*([^\n]+)/i); const triggers = triggerMatch ? triggerMatch[1].trim() : "General use"; + // Extract overview section (everything after first ---) + const overviewMatch = content.match(/---\n([\s\S]+?)(?=\n##|\n#|$)/i); + let overview = ""; + if (overviewMatch) { + overview = overviewMatch[1].trim(); + // Clean up frontmatter keys + overview = overview.replace(/^[a-z]+:/gim, "").trim(); + } + + // Extract all ## sections + const sections: Record = {}; + const sectionMatches = content.matchAll(/##\s+(.+?)\n([\s\S]*?)(?=##\s+|$)/gi); + for (const match of sectionMatches) { + const title = match[1].trim(); + const body = match[2].trim(); + if (title.toLowerCase() !== "overview" && body) { + sections[title] = body.substring(0, 500); + } + } + + // Get scripts info + const scriptsDir = path.join(tempPath, slug, "scripts"); + let scripts: string[] = []; + if (existsSync(scriptsDir)) { + try { + const files = await readdir(scriptsDir); + scripts = files.filter(f => !f.startsWith(".")); + } catch {} + } + await execAsync(`rm -rf ${tempPath} 2>/dev/null`); - // Project matching + // Project matching - more thorough analysis const projectsDir = "/root/.openclaw/workspace/projects"; - const projectMatches: string[] = []; + const projectMatches: Array<{name: string; relevance: string; why: string}> = []; try { const entries = await readdir(projectsDir, { withFileTypes: true }); for (const entry of entries) { - if (entry.isFile() && (entry.name.endsWith(".md") || entry.name.endsWith(".json"))) { + if (entry.isFile() && entry.name.endsWith(".md")) { const projectPath = path.join(projectsDir, entry.name); const projectContent = await readFile(projectPath, "utf-8"); - const projectName = entry.name.replace(/\.(md|json)$/, ""); + const projectName = entry.name.replace(/\.md$/, ""); - const skillKeywords = (desc + " " + triggers).toLowerCase(); + // Check purpose/goals + const purposeMatch = projectContent.match(/purpose:?\s*([^\n]+)/i); + const purpose = purposeMatch ? purposeMatch[1].trim() : ""; + + // Find keyword matches with skill content + const skillContent = (desc + " " + triggers + " " + overview).toLowerCase(); const projectKeywords = projectContent.toLowerCase(); - const overlaps: string[] = []; - const skillWords = skillKeywords.split(/\s+/).filter((w: string) => w.length > 4); - for (const word of skillWords) { - if (projectKeywords.includes(word)) overlaps.push(word); + // Look for specific relevance + const relevantKeywords: string[] = []; + const keywordsToCheck = ["automation", "workflow", "ai", "agent", "service", "client", "sales", "marketing", "content", "data", "email", "task", "report", "integration", "api", "web", "voice", "chat"]; + + for (const kw of keywordsToCheck) { + if (skillContent.includes(kw) && projectKeywords.includes(kw)) { + relevantKeywords.push(kw); + } } - if (overlaps.length >= 2) { - projectMatches.push(`${projectName}: ${overlaps.slice(0, 3).join(", ")}`); + if (relevantKeywords.length >= 2 || (purpose && relevantKeywords.length >= 1)) { + const why = relevantKeywords.slice(0, 4).join(", "); + projectMatches.push({ + name: projectName, + relevance: `${relevantKeywords.length} keyword matches`, + why: `Useful for: ${why}` + }); } } } } catch {} - return NextResponse.json({ description: desc, triggers: triggers, raw: content.substring(0, 2000), projectMatches }); + return NextResponse.json({ + description: desc, + triggers: triggers, + overview: overview.substring(0, 1000), + sections, + scripts, + projectMatches + }); } catch (e: any) { return NextResponse.json({ error: e.message, description: "Could not analyze skill" }); } diff --git a/app/mission-control/clawhub/page.tsx b/app/mission-control/clawhub/page.tsx index b419561..ad58b45 100644 --- a/app/mission-control/clawhub/page.tsx +++ b/app/mission-control/clawhub/page.tsx @@ -215,17 +215,45 @@ export default function ClawHubMarketplace() { description: data.description, triggers: data.triggers }); - let output = `📋 ${selectedSkill.name}\n\n`; - output += `Description: ${data.description || "N/A"}\n\n`; - output += `Triggers: ${data.triggers || "N/A"}\n\n`; + + let output = `📋 ${selectedSkill.name}\n`; + output += `${"─".repeat(40)}\n\n`; + output += `💬 ${data.description || "N/A"}\n\n`; + output += `🎯 Triggers: ${data.triggers || "N/A"}\n\n`; + + if (data.overview) { + output += `📝 Overview:\n${data.overview.substring(0, 500)}${data.overview.length > 500 ? "..." : ""}\n\n`; + } + + if (data.scripts && data.scripts.length > 0) { + output += `🛠️ Scripts: ${data.scripts.join(", ")}\n\n`; + } + + if (data.sections) { + const relevantSections = Object.entries(data.sections).filter(([k]) => + !["overview", "triggers", "description", "guardrails"].includes(k.toLowerCase()) + ); + if (relevantSections.length > 0) { + output += `📚 Sections:\n`; + relevantSections.forEach(([title, body]) => { + output += ` ▸ ${title}\n`; + }); + output += `\n`; + } + } + if (data.projectMatches && data.projectMatches.length > 0) { output += `🎯 Project Matches:\n`; - data.projectMatches.forEach((m: string) => { - output += ` • ${m}\n`; + output += `${"─".repeat(40)}\n`; + data.projectMatches.forEach((m: {name: string; relevance: string; why: string}) => { + output += `\n 📦 ${m.name.toUpperCase()}\n`; + output += ` ${m.why}\n`; }); output += `\n`; + } else { + output += `❌ No direct project matches found\n`; } - output += (data.raw ? `📄 SKILL.md Preview:\n${data.raw.substring(0, 500)}...` : ""); + setOutput(output); } catch (e) { setOutput("Error: " + String(e)); diff --git a/data/eod_briefs.json b/data/eod_briefs.json index cd0924d..8c834fe 100644 --- a/data/eod_briefs.json +++ b/data/eod_briefs.json @@ -1,4 +1,16 @@ [ + { + "id": "eod-1774382402686", + "date": "2026-03-24", + "completed": [ + "See Mission Control for details", + "🛡️ SECURITY: 10 advisories found today! Check MC." + ], + "progress": {}, + "council": {}, + "tomorrow": [], + "created_at": "2026-03-24T20:00:02.686Z" + }, { "id": "eod-1774296002482", "date": "2026-03-23",