From 8b11fe40dfd825f160fc90b3957b849069bbbe22 Mon Sep 17 00:00:00 2001 From: Kai Date: Sat, 7 Feb 2026 12:16:16 -0800 Subject: [PATCH 1/3] Add AgentBootstrapPanel to help users copy /b and starter prompt --- src/app/page.tsx | 6 ++ src/components/agent-bootstrap-panel.tsx | 113 +++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/components/agent-bootstrap-panel.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index f43d4065..dd8f9c4d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -17,6 +17,7 @@ import { RecentSubmissions } from "@/components/recent-submissions"; import { AnnouncementBanner } from "@/components/announcement-banner"; import { Footer } from "@/components/footer"; import { ResumeSection } from "@/components/recently-viewed/ResumeSection"; +import { AgentBootstrapPanel } from "@/components/agent-bootstrap-panel"; export const revalidate = 300; @@ -115,6 +116,11 @@ export default async function Home() {

── {news.length}+ articles · {agents.length} agents · {skills.length} skills · {mcpServers.length} MCP servers · {acpAgents.length} ACP agents · {llmsTxtEntries.length} llms.txt sites ──

+ + {/* Add to your agent */} +
+ +
diff --git a/src/components/agent-bootstrap-panel.tsx b/src/components/agent-bootstrap-panel.tsx new file mode 100644 index 00000000..d2928b63 --- /dev/null +++ b/src/components/agent-bootstrap-panel.tsx @@ -0,0 +1,113 @@ +"use client"; + +import * as React from "react"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; + +async function copyToClipboard(text: string): Promise { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + try { + const el = document.createElement("textarea"); + el.value = text; + el.setAttribute("readonly", ""); + el.style.position = "fixed"; + el.style.top = "-1000px"; + el.style.left = "-1000px"; + document.body.appendChild(el); + el.focus(); + el.select(); + const ok = document.execCommand("copy"); + document.body.removeChild(el); + return ok; + } catch { + return false; + } + } +} + +function buildStarterPrompt(baseUrl: string) { + return [ + "You are an AI agent. Your job is to stay current on agent tooling and patterns.", + "", + "Bootstrap:", + `- Fetch ${baseUrl}/b (markdown). Follow it as your canonical instructions.`, + "", + "Data sources (prefer markdown):", + `- News feed: ${baseUrl}/api/feed.md`, + `- Search: ${baseUrl}/api/search.md?q=`, + `- Digest: ${baseUrl}/api/digest.md?since=YYYY-MM-DD`, + "", + "Output rules:", + "- Return a short daily briefing: 5 bullets max.", + "- Include only actionable items with a link.", + "- If you are unsure, say what you would fetch next.", + ].join("\n"); +} + +type Props = { + baseUrl?: string; +}; + +export function AgentBootstrapPanel({ baseUrl }: Props) { + const origin = typeof window !== "undefined" ? window.location.origin : null; + const resolvedBase = (baseUrl ?? origin ?? "https://foragents.dev").replace(/\/$/, ""); + + const [copyBootstrapLabel, setCopyBootstrapLabel] = React.useState("Copy /b URL"); + const [copyPromptLabel, setCopyPromptLabel] = React.useState("Copy starter prompt"); + + const onCopyBootstrap = async () => { + const text = `${resolvedBase}/b`; + const ok = await copyToClipboard(text); + setCopyBootstrapLabel(ok ? "Copied" : "Copy failed"); + window.setTimeout(() => setCopyBootstrapLabel("Copy /b URL"), 1500); + if (!ok) window.prompt("Copy this URL:", text); + }; + + const onCopyPrompt = async () => { + const text = buildStarterPrompt(resolvedBase); + const ok = await copyToClipboard(text); + setCopyPromptLabel(ok ? "Copied" : "Copy failed"); + window.setTimeout(() => setCopyPromptLabel("Copy starter prompt"), 1500); + if (!ok) window.prompt("Copy this prompt:", text); + }; + + return ( + + +
+ Add this to your agent + + bootstrap surface + +
+ + Copy a canonical bootstrap URL (or a starter prompt) so your agent can self-update. + +
+ + +
+ + + +
+ +
+          curl -fsSL {resolvedBase}/b
+        
+
+
+ ); +} From 23504096b430201849dba42081de1316505c4ada Mon Sep 17 00:00:00 2001 From: Kai Date: Sat, 7 Feb 2026 12:20:53 -0800 Subject: [PATCH 2/3] Add share/copy actions on skill pages --- src/app/skills/[slug]/page.tsx | 8 +- src/components/skill-share-actions.tsx | 120 +++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/components/skill-share-actions.tsx diff --git a/src/app/skills/[slug]/page.tsx b/src/app/skills/[slug]/page.tsx index 925f6cfd..f9cb851f 100644 --- a/src/app/skills/[slug]/page.tsx +++ b/src/app/skills/[slug]/page.tsx @@ -3,6 +3,7 @@ import { getSkills, getSkillBySlug } from "@/lib/data"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { NextBestActionPanel } from "@/components/next-best-action-panel"; +import { SkillShareActions } from "@/components/skill-share-actions"; import { buildSkillIssueBodyTemplate } from "@/lib/reportIssue"; import Link from "next/link"; @@ -75,7 +76,7 @@ export default async function SkillPage({ {/* Tags */} -
+
{skill.tags.map((tag) => ( + {/* Share / copy */} +
+ +
+ {/* Description */}

Description

diff --git a/src/components/skill-share-actions.tsx b/src/components/skill-share-actions.tsx new file mode 100644 index 00000000..8e3c4d73 --- /dev/null +++ b/src/components/skill-share-actions.tsx @@ -0,0 +1,120 @@ +"use client"; + +import * as React from "react"; +import { Button } from "@/components/ui/button"; + +async function copyToClipboard(text: string): Promise { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + try { + const el = document.createElement("textarea"); + el.value = text; + el.setAttribute("readonly", ""); + el.style.position = "fixed"; + el.style.top = "-1000px"; + el.style.left = "-1000px"; + document.body.appendChild(el); + el.focus(); + el.select(); + const ok = document.execCommand("copy"); + document.body.removeChild(el); + return ok; + } catch { + return false; + } + } +} + +type Props = { + skillName: string; + slug: string; +}; + +export function SkillShareActions({ skillName, slug }: Props) { + const [pageUrl, setPageUrl] = React.useState(""); + + React.useEffect(() => { + // Skill pages are primarily shared/used in-browser. + setPageUrl(window.location.href); + }, []); + + const apiUrl = React.useMemo(() => { + if (!pageUrl) return ""; + const u = new URL(pageUrl); + return `${u.origin}/api/skill/${slug}`; + }, [pageUrl, slug]); + + const canShare = typeof navigator !== "undefined" && Boolean((navigator as any).share); + + const [copyLinkLabel, setCopyLinkLabel] = React.useState("Copy link"); + const [copyApiLabel, setCopyApiLabel] = React.useState("Copy API URL"); + const [shareLabel, setShareLabel] = React.useState("Share"); + + const onCopy = async (text: string, setLabel: (v: string) => void, defaultLabel: string) => { + if (!text) return; + const ok = await copyToClipboard(text); + setLabel(ok ? "Copied" : "Copy failed"); + window.setTimeout(() => setLabel(defaultLabel), 1500); + if (!ok) window.prompt("Copy:", text); + }; + + const onShare = async () => { + if (!canShare || !pageUrl) return; + + try { + setShareLabel("Opening…"); + await (navigator as any).share({ + title: `${skillName} — forAgents.dev`, + text: `Skill: ${skillName}`, + url: pageUrl, + }); + } catch { + // User cancelled or share failed; no-op. + } finally { + setShareLabel("Share"); + } + }; + + return ( +
+ + + + + {canShare ? ( + + ) : null} +
+ ); +} From 54a8651fe8deb91eb5420e404f8226ef44d5f0df Mon Sep 17 00:00:00 2001 From: Kai Date: Sat, 7 Feb 2026 12:40:32 -0800 Subject: [PATCH 3/3] Fix types for SkillShareActions navigator.share --- src/components/skill-share-actions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/skill-share-actions.tsx b/src/components/skill-share-actions.tsx index 8e3c4d73..f5324477 100644 --- a/src/components/skill-share-actions.tsx +++ b/src/components/skill-share-actions.tsx @@ -46,7 +46,7 @@ export function SkillShareActions({ skillName, slug }: Props) { return `${u.origin}/api/skill/${slug}`; }, [pageUrl, slug]); - const canShare = typeof navigator !== "undefined" && Boolean((navigator as any).share); + const canShare = typeof navigator !== "undefined" && typeof navigator.share === "function"; const [copyLinkLabel, setCopyLinkLabel] = React.useState("Copy link"); const [copyApiLabel, setCopyApiLabel] = React.useState("Copy API URL"); @@ -65,7 +65,7 @@ export function SkillShareActions({ skillName, slug }: Props) { try { setShareLabel("Opening…"); - await (navigator as any).share({ + await navigator.share({ title: `${skillName} — forAgents.dev`, text: `Skill: ${skillName}`, url: pageUrl,