feat: Enhanced analyze - shows overview, scripts, sections, better project matching
This commit is contained in:
+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" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user