feat: Add sync/push buttons to ClawHub UI

This commit is contained in:
2026-03-24 18:16:07 +01:00
parent 064812d730
commit 4c699a65a4
2 changed files with 85 additions and 7 deletions
+25
View File
@@ -56,6 +56,13 @@ export async function GET(req: NextRequest) {
const agents = ["horus", "amun", "cleo"]; const agents = ["horus", "amun", "cleo"];
const installed: Record<string, string[]> = {}; const installed: Record<string, string[]> = {};
// 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) { for (const agent of agents) {
const agentPath = path.join(SKILLS_BASE, agent); const agentPath = path.join(SKILLS_BASE, agent);
if (existsSync(agentPath)) { if (existsSync(agentPath)) {
@@ -79,6 +86,24 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ info: stdout }); 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 }); return NextResponse.json({ error: "Unknown action" }, { status: 400 });
} catch (e: unknown) { } catch (e: unknown) {
const error = e as Error & { stdout?: string; message?: string }; const error = e as Error & { stdout?: string; message?: string };
+60 -7
View File
@@ -23,9 +23,10 @@ export default function ClawHubMarketplace() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [selectedSkill, setSelectedSkill] = useState<Skill | null>(null); const [selectedSkill, setSelectedSkill] = useState<Skill | null>(null);
const [selectedAgents, setSelectedAgents] = useState<string[]>(["horus"]); const [selectedAgents, setSelectedAgents] = useState<string[]>(["horus"]);
const [action, setAction] = useState<"install" | "delete" | null>(null); const [action, setAction] = useState<"install" | "delete" | "sync" | "push" | null>(null);
const [output, setOutput] = useState(""); const [output, setOutput] = useState("");
const [installedSkills, setInstalledSkills] = useState<Record<string, string[]>>({}); const [installedSkills, setInstalledSkills] = useState<Record<string, string[]>>({});
const [syncing, setSyncing] = useState(false);
const fetchInstalled = useCallback(async () => { const fetchInstalled = useCallback(async () => {
try { try {
@@ -120,6 +121,43 @@ export default function ClawHubMarketplace() {
setAction(null); 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) => { const isInstalled = (slug: string) => {
return AGENTS.some((agent) => installedSkills[agent]?.includes(slug)); return AGENTS.some((agent) => installedSkills[agent]?.includes(slug));
}; };
@@ -138,12 +176,27 @@ export default function ClawHubMarketplace() {
<div className="bg-slate-800 rounded-xl p-4 mb-6 border border-slate-700"> <div className="bg-slate-800 rounded-xl p-4 mb-6 border border-slate-700">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<h2 className="font-semibold text-lg">Agent Skills Status</h2> <h2 className="font-semibold text-lg">Agent Skills Status</h2>
<button <div className="flex gap-2">
onClick={fetchInstalled} <button
className="text-sm bg-slate-700 hover:bg-slate-600 px-3 py-1 rounded transition" onClick={handleSync}
> disabled={syncing}
🔄 Refresh className="text-sm bg-blue-700 hover:bg-blue-600 disabled:bg-blue-800 px-3 py-1 rounded transition flex items-center gap-1"
</button> >
{syncing ? "⏳" : "🔄"} Sync
</button>
<button
onClick={handlePush}
className="text-sm bg-green-700 hover:bg-green-600 px-3 py-1 rounded transition"
>
Push
</button>
<button
onClick={fetchInstalled}
className="text-sm bg-slate-700 hover:bg-slate-600 px-3 py-1 rounded transition"
>
Refresh
</button>
</div>
</div> </div>
<div className="grid grid-cols-3 gap-4"> <div className="grid grid-cols-3 gap-4">
{AGENTS.map((agent) => ( {AGENTS.map((agent) => (