feat: Enhanced analyze - shows overview, scripts, sections, better project matching
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"agentic-workflow-automation": {
|
||||
"version": "0.1.0",
|
||||
"installedAt": 1774375021538
|
||||
"installedAt": 1774385419847
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+66
-12
@@ -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<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`);
|
||||
|
||||
// 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" });
|
||||
}
|
||||
|
||||
@@ -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`;
|
||||
if (data.projectMatches && data.projectMatches.length > 0) {
|
||||
output += `🎯 Project Matches:\n`;
|
||||
data.projectMatches.forEach((m: string) => {
|
||||
output += ` • ${m}\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`;
|
||||
}
|
||||
output += (data.raw ? `📄 SKILL.md Preview:\n${data.raw.substring(0, 500)}...` : "");
|
||||
}
|
||||
|
||||
if (data.projectMatches && data.projectMatches.length > 0) {
|
||||
output += `🎯 Project Matches:\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`;
|
||||
}
|
||||
|
||||
setOutput(output);
|
||||
} catch (e) {
|
||||
setOutput("Error: " + String(e));
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user