diff --git a/app/api/clawhub/route.ts b/app/api/clawhub/route.ts index 1dba815..0704db6 100644 --- a/app/api/clawhub/route.ts +++ b/app/api/clawhub/route.ts @@ -56,6 +56,13 @@ export async function GET(req: NextRequest) { const agents = ["horus", "amun", "cleo"]; const installed: Record = {}; + // Sync Horus repo first + try { + await execAsync(`cd ${SKILLS_BASE} && git pull origin main 2>&1`); + } catch {} + + for (const agent of agents) { + for (const agent of agents) { const agentPath = path.join(SKILLS_BASE, agent); if (existsSync(agentPath)) { @@ -79,6 +86,24 @@ export async function GET(req: NextRequest) { return NextResponse.json({ info: stdout }); } + if (action === "sync") { + try { + const { stdout, stderr } = await execAsync(`cd ${SKILLS_BASE} && git pull origin main 2>&1`); + return NextResponse.json({ output: stdout || "Already up to date", error: stderr }); + } catch (e: any) { + return NextResponse.json({ output: "Sync failed", error: e.message }); + } + } + + if (action === "push") { + try { + const { stdout, stderr } = await execAsync(`cd ${SKILLS_BASE} && git add -A && git commit -m "Update skills" && git push origin main 2>&1`); + return NextResponse.json({ output: stdout || "Pushed", error: stderr }); + } catch (e: any) { + return NextResponse.json({ output: "Push failed", error: e.message }); + } + } + return NextResponse.json({ error: "Unknown action" }, { status: 400 }); } catch (e: unknown) { const error = e as Error & { stdout?: string; message?: string }; diff --git a/app/mission-control/clawhub/page.tsx b/app/mission-control/clawhub/page.tsx index 1b0f28d..46f937f 100644 --- a/app/mission-control/clawhub/page.tsx +++ b/app/mission-control/clawhub/page.tsx @@ -23,9 +23,10 @@ export default function ClawHubMarketplace() { const [loading, setLoading] = useState(false); const [selectedSkill, setSelectedSkill] = useState(null); const [selectedAgents, setSelectedAgents] = useState(["horus"]); - const [action, setAction] = useState<"install" | "delete" | null>(null); + const [action, setAction] = useState<"install" | "delete" | "sync" | "push" | null>(null); const [output, setOutput] = useState(""); const [installedSkills, setInstalledSkills] = useState>({}); + const [syncing, setSyncing] = useState(false); const fetchInstalled = useCallback(async () => { try { @@ -120,6 +121,43 @@ export default function ClawHubMarketplace() { setAction(null); }; + const handleSync = async () => { + setSyncing(true); + setAction("sync"); + setOutput("Syncing with GitHub...\n"); + try { + const res = await fetch("/api/clawhub", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "sync" }), + }); + const data = await res.json(); + setOutput(data.output || data.error || "Sync complete"); + fetchInstalled(); + } catch (e) { + setOutput("Error: " + String(e)); + } + setAction(null); + setSyncing(false); + }; + + const handlePush = async () => { + setAction("push"); + setOutput("Pushing to GitHub...\n"); + try { + const res = await fetch("/api/clawhub", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "push" }), + }); + const data = await res.json(); + setOutput(data.output || data.error || "Push complete"); + } catch (e) { + setOutput("Error: " + String(e)); + } + setAction(null); + }; + const isInstalled = (slug: string) => { return AGENTS.some((agent) => installedSkills[agent]?.includes(slug)); }; @@ -138,12 +176,27 @@ export default function ClawHubMarketplace() {

Agent Skills Status

- +
+ + + +
{AGENTS.map((agent) => (