feat: Enhanced analyze - shows overview, scripts, sections, better project matching

This commit is contained in:
2026-03-24 21:59:53 +01:00
parent 48a452b994
commit fc6ddbd1b2
4 changed files with 113 additions and 19 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
}, },
"agentic-workflow-automation": { "agentic-workflow-automation": {
"version": "0.1.0", "version": "0.1.0",
"installedAt": 1774375021538 "installedAt": 1774385419847
} }
} }
} }
+66 -12
View File
@@ -132,6 +132,7 @@ export async function POST(req: NextRequest) {
try { try {
await execAsync(`rm -rf ${tempPath} 2>/dev/null; ${CLAWHUB_BIN} install ${slug} --dir ${tempPath} 2>&1`); 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"); const skillMdPath = path.join(tempPath, slug, "SKILL.md");
let content = ""; let content = "";
if (existsSync(skillMdPath)) { if (existsSync(skillMdPath)) {
@@ -141,43 +142,96 @@ export async function POST(req: NextRequest) {
if (existsSync(altPath)) content = await readFile(altPath, "utf-8"); if (existsSync(altPath)) content = await readFile(altPath, "utf-8");
} }
// Extract description from frontmatter
const descMatch = content.match(/description:\s*([^\n]+)/i); const descMatch = content.match(/description:\s*([^\n]+)/i);
const desc = descMatch ? descMatch[1].trim() : "No description available"; const desc = descMatch ? descMatch[1].trim() : "No description available";
// Extract triggers
const triggerMatch = content.match(/triggers?:\s*([^\n]+)/i); const triggerMatch = content.match(/triggers?:\s*([^\n]+)/i);
const triggers = triggerMatch ? triggerMatch[1].trim() : "General use"; 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<string, string> = {};
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`); await execAsync(`rm -rf ${tempPath} 2>/dev/null`);
// Project matching // Project matching - more thorough analysis
const projectsDir = "/root/.openclaw/workspace/projects"; const projectsDir = "/root/.openclaw/workspace/projects";
const projectMatches: string[] = []; const projectMatches: Array<{name: string; relevance: string; why: string}> = [];
try { try {
const entries = await readdir(projectsDir, { withFileTypes: true }); const entries = await readdir(projectsDir, { withFileTypes: true });
for (const entry of entries) { 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 projectPath = path.join(projectsDir, entry.name);
const projectContent = await readFile(projectPath, "utf-8"); 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 projectKeywords = projectContent.toLowerCase();
const overlaps: string[] = []; // Look for specific relevance
const skillWords = skillKeywords.split(/\s+/).filter((w: string) => w.length > 4); const relevantKeywords: string[] = [];
for (const word of skillWords) { const keywordsToCheck = ["automation", "workflow", "ai", "agent", "service", "client", "sales", "marketing", "content", "data", "email", "task", "report", "integration", "api", "web", "voice", "chat"];
if (projectKeywords.includes(word)) overlaps.push(word);
for (const kw of keywordsToCheck) {
if (skillContent.includes(kw) && projectKeywords.includes(kw)) {
relevantKeywords.push(kw);
}
} }
if (overlaps.length >= 2) { if (relevantKeywords.length >= 2 || (purpose && relevantKeywords.length >= 1)) {
projectMatches.push(`${projectName}: ${overlaps.slice(0, 3).join(", ")}`); const why = relevantKeywords.slice(0, 4).join(", ");
projectMatches.push({
name: projectName,
relevance: `${relevantKeywords.length} keyword matches`,
why: `Useful for: ${why}`
});
} }
} }
} }
} catch {} } 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) { } catch (e: any) {
return NextResponse.json({ error: e.message, description: "Could not analyze skill" }); return NextResponse.json({ error: e.message, description: "Could not analyze skill" });
} }
+34 -6
View File
@@ -215,17 +215,45 @@ export default function ClawHubMarketplace() {
description: data.description, description: data.description,
triggers: data.triggers triggers: data.triggers
}); });
let output = `📋 ${selectedSkill.name}\n\n`;
output += `Description: ${data.description || "N/A"}\n\n`; let output = `📋 ${selectedSkill.name}\n`;
output += `Triggers: ${data.triggers || "N/A"}\n\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) { if (data.projectMatches && data.projectMatches.length > 0) {
output += `🎯 Project Matches:\n`; output += `🎯 Project Matches:\n`;
data.projectMatches.forEach((m: string) => { output += `${"─".repeat(40)}\n`;
output += `${m}\n`; data.projectMatches.forEach((m: {name: string; relevance: string; why: string}) => {
output += `\n 📦 ${m.name.toUpperCase()}\n`;
output += ` ${m.why}\n`;
}); });
output += `\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); setOutput(output);
} catch (e) { } catch (e) {
setOutput("Error: " + String(e)); setOutput("Error: " + String(e));
+12
View File
@@ -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", "id": "eod-1774296002482",
"date": "2026-03-23", "date": "2026-03-23",