From 5ece3001a34267afbe7ba5d7e9e178d2e05c0843 Mon Sep 17 00:00:00 2001 From: Horus AI Date: Tue, 24 Mar 2026 18:40:40 +0100 Subject: [PATCH] feat: ClawHub categories view with top skills per category --- .clawhub/lock.json | 9 + app/mission-control/clawhub/page.tsx | 383 ++++++++++++++++++--------- 2 files changed, 263 insertions(+), 129 deletions(-) create mode 100644 .clawhub/lock.json diff --git a/.clawhub/lock.json b/.clawhub/lock.json new file mode 100644 index 0000000..cd3e573 --- /dev/null +++ b/.clawhub/lock.json @@ -0,0 +1,9 @@ +{ + "version": 1, + "skills": { + "automation-workflows": { + "version": "0.1.0", + "installedAt": 1774373495681 + } + } +} diff --git a/app/mission-control/clawhub/page.tsx b/app/mission-control/clawhub/page.tsx index 46f937f..ee9ac9d 100644 --- a/app/mission-control/clawhub/page.tsx +++ b/app/mission-control/clawhub/page.tsx @@ -7,7 +7,7 @@ interface Skill { slug: string; name: string; score: number; - description?: string; + category?: string; } const AGENTS = ["horus", "amun", "cleo"] as const; @@ -17,16 +17,39 @@ const AGENT_LABELS: Record = { cleo: "👑 Cleo", }; +const CATEGORIES = [ + { id: "productivity", name: "⚡ Productivity", icon: "⚡" }, + { id: "automation", name: "🤖 Automation", icon: "🤖" }, + { id: "ai", name: "🧠 AI", icon: "🧠" }, + { id: "writing", name: "✍️ Writing", icon: "✍️" }, + { id: "research", name: "🔬 Research", icon: "🔬" }, + { id: "coding", name: "💻 Coding", icon: "💻" }, + { id: "business", name: "💼 Business", icon: "💼" }, +]; + +const CATEGORY_QUERIES: Record = { + productivity: "productivity focus work", + automation: "automation workflow", + ai: "artificial intelligence machine learning", + writing: "writing content copy", + research: "research data analysis", + coding: "code programming developer", + business: "business sales marketing", +}; + export default function ClawHubMarketplace() { const [skills, setSkills] = useState([]); + const [categorySkills, setCategorySkills] = useState>({}); const [search, setSearch] = useState(""); const [loading, setLoading] = useState(false); const [selectedSkill, setSelectedSkill] = useState(null); const [selectedAgents, setSelectedAgents] = useState(["horus"]); + const [view, setView] = useState<"popular" | "categories" | "search">("popular"); const [action, setAction] = useState<"install" | "delete" | "sync" | "push" | null>(null); const [output, setOutput] = useState(""); const [installedSkills, setInstalledSkills] = useState>({}); const [syncing, setSyncing] = useState(false); + const [selectedCategory, setSelectedCategory] = useState(null); const fetchInstalled = useCallback(async () => { try { @@ -50,22 +73,58 @@ export default function ClawHubMarketplace() { setLoading(false); }, []); + const fetchAllCategories = useCallback(async () => { + setLoading(true); + const results: Record = {}; + + for (const cat of CATEGORIES) { + try { + const res = await fetch(`/api/clawhub?action=search&q=${encodeURIComponent(CATEGORY_QUERIES[cat.id])}`); + const data = await res.json(); + results[cat.id] = (data.skills || []).slice(0, 5); // Top 5 per category + } catch (e) { + results[cat.id] = []; + } + } + + setCategorySkills(results); + setLoading(false); + }, []); + const refreshPopular = useCallback(async () => { setLoading(true); - try { - const res = await fetch("/api/clawhub?action=popular"); - const data = await res.json(); - setSkills(data.skills || []); - } catch (e) { - console.error(e); + setView("popular"); + // Get top skills from all categories combined + const allSkills: Skill[] = []; + const seen = new Set(); + + for (const cat of CATEGORIES) { + try { + const res = await fetch(`/api/clawhub?action=search&q=${encodeURIComponent(CATEGORY_QUERIES[cat.id])}`); + const data = await res.json(); + for (const skill of data.skills || []) { + if (!seen.has(skill.slug)) { + seen.add(skill.slug); + allSkills.push({ ...skill, category: cat.id }); + } + } + } catch {} } + + // Sort by score and take top 30 + allSkills.sort((a, b) => b.score - a.score); + setSkills(allSkills.slice(0, 30)); setLoading(false); }, []); useEffect(() => { - refreshPopular(); + if (view === "popular") { + refreshPopular(); + } else if (view === "categories") { + fetchAllCategories(); + } fetchInstalled(); - }, [refreshPopular, fetchInstalled]); + }, [view, refreshPopular, fetchAllCategories, fetchInstalled]); const toggleAgent = (agent: string) => { setSelectedAgents((prev) => @@ -162,6 +221,10 @@ export default function ClawHubMarketplace() { return AGENTS.some((agent) => installedSkills[agent]?.includes(slug)); }; + const getCategoryName = (catId: string) => { + return CATEGORIES.find((c) => c.id === catId)?.name || catId; + }; + return (
@@ -214,150 +277,212 @@ export default function ClawHubMarketplace() {
+ {/* View Toggle */} +
+ + +
+ {/* Search */}
setSearch(e.target.value)} - onKeyDown={(e) => e.key === "Enter" && searchSkills(search)} + onKeyDown={(e) => { if (e.key === "Enter") { searchSkills(search); setView("search"); } }} placeholder="Search skills..." className="flex-1 bg-slate-800 border border-slate-700 rounded-lg px-4 py-2 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500" /> -
-
- {/* Skills List */} -
-
-

Available Skills

-

{skills.length} skills found

-
-
- {loading ? ( -
Loading...
- ) : skills.length === 0 ? ( -
No skills found
- ) : ( - skills.map((skill) => { - const installed = isInstalled(skill.slug); - return ( - - ); - }) - )} -
-
- - {/* Detail Panel */} -
-
-

- {selectedSkill ? selectedSkill.name : "Select a Skill"} -

- {selectedSkill && ( -

@{selectedSkill.slug}

- )} -
- - {!selectedSkill ? ( -
- Click a skill to see details -
- ) : ( -
- {/* Agent Selector */} -
-

Install On:

-
- {AGENTS.map((agent) => ( + {/* Categories View */} + {view === "categories" && ( +
+ {CATEGORIES.map((cat) => ( +
+
+

{cat.name}

+

{categorySkills[cat.id]?.length || 0} skills

+
+
+ {(categorySkills[cat.id] || []).map((skill) => { + const installed = isInstalled(skill.slug); + return ( - ))} -
- + ); + })}
+
+ ))} +
+ )} - {/* Action Buttons */} -
- - -
- - {/* Output */} - {output && ( -
-

Output

-
-                      {output}
-                    
-
+ {/* Popular/Search View */} + {(view === "popular" || view === "search") && ( +
+ {/* Skills List */} +
+
+

+ {view === "popular" ? "🔥 Popular Skills" : "Search Results"} +

+

{skills.length} skills found

+
+
+ {loading ? ( +
Loading...
+ ) : skills.length === 0 ? ( +
No skills found
+ ) : ( + skills.map((skill) => { + const installed = isInstalled(skill.slug); + return ( + + ); + }) )}
- )} +
+ + {/* Detail Panel */} +
+
+

+ {selectedSkill ? selectedSkill.name : "Select a Skill"} +

+ {selectedSkill && ( +

@{selectedSkill.slug}

+ )} +
+ + {!selectedSkill ? ( +
+ Click a skill to see details +
+ ) : ( +
+ {/* Agent Selector */} +
+

Install On:

+
+ {AGENTS.map((agent) => ( + + ))} +
+ +
+ + {/* Action Buttons */} +
+ + +
+ + {/* Output */} + {output && ( +
+

Output

+
+                        {output}
+                      
+
+ )} +
+ )} +
-
+ )}
);