From 2eeea9a0cd8665bbe0e6c80f08603425498757e5 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sun, 21 Jun 2026 17:24:17 +0300 Subject: feat: add plugin marketplace --- .github/workflows/ci.yml | 6 + .gitignore | 3 + AGENTS.md | 6 +- docs/next/website/src/content/docs/marketplace.mdx | 54 +- docs/next/website/src/content/docs/plugins.mdx | 15 +- justfile | 6 + website/css/style.css | 392 +++++--------- website/index.html | 272 ++++------ website/src/components/MarketingLayout.astro | 6 +- website/src/components/SiteTitle.astro | 2 +- website/src/content.config.ts | 11 - website/src/content/blog/herdr-0-6-3.md | 85 +++ website/src/content/docs/marketplace.mdx | 54 +- website/src/content/docs/plugins.mdx | 15 +- website/src/content/releases/v0-6-3.md | 86 --- website/src/data/plugins-fixture.json | 139 +++++ website/src/pages/compare.astro | 6 +- website/src/pages/plugins.astro | 601 +++++++++++++++++++++ website/src/pages/releases/[...slug].astro | 89 --- website/src/pages/releases/index.astro | 42 -- workers/plugin-marketplace/package.json | 10 + workers/plugin-marketplace/r2-cors.json | 13 + workers/plugin-marketplace/src/index.test.ts | 260 +++++++++ workers/plugin-marketplace/src/index.ts | 349 ++++++++++++ workers/plugin-marketplace/wrangler.toml | 11 + 25 files changed, 1817 insertions(+), 716 deletions(-) create mode 100644 website/src/content/blog/herdr-0-6-3.md delete mode 100644 website/src/content/releases/v0-6-3.md create mode 100644 website/src/data/plugins-fixture.json create mode 100644 website/src/pages/plugins.astro delete mode 100644 website/src/pages/releases/[...slug].astro delete mode 100644 website/src/pages/releases/index.astro create mode 100644 workers/plugin-marketplace/package.json create mode 100644 workers/plugin-marketplace/r2-cors.json create mode 100644 workers/plugin-marketplace/src/index.test.ts create mode 100644 workers/plugin-marketplace/src/index.ts create mode 100644 workers/plugin-marketplace/wrangler.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40269131..1bfc7aa6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,12 @@ jobs: with: tool: just,cargo-nextest + - name: Install Bun + if: matrix.kind == 'unix' + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: 1.3.14 + - name: Install Zig uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 with: diff --git a/.gitignore b/.gitignore index 4965821d..e59f3475 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ __pycache__/ /.pi/cache /result /result-* + +/.wrangler/ +**/.wrangler/ diff --git a/AGENTS.md b/AGENTS.md index 3c7d158b..ef89e0f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # herdr -Terminal workspace manager for AI coding agents. Rust + ratatui. +Terminal based agent runtime for coding agents. ## Principles @@ -154,8 +154,8 @@ The release workflows must publish these four assets: ## External contributor guardrail -Before opening an issue, opening a PR, or pushing branches to this repository, detect the acting GitHub account when possible. Check `gh auth status`, the configured git remote, or the available environment context. If the acting account is not `ogulcancelik`, treat the human as an external contributor unless this is clearly a private or custom fork. +Before opening an issue, opening a PR, or pushing branches to this repository, detect the acting GitHub account when possible. Check `gh auth status`, the configured git remote, or the available environment context. If the acting account is not `ogulcancelik`, treat the human as an *external contributor* unless this is clearly a private or custom fork. External contributors must follow `CONTRIBUTING.md` strictly. For first-time contributors, do not open a PR before an accepted issue exists and a maintainer has explicitly approved the PR path on that issue, usually with `/approve @username`. Feature requests, ideas, questions, and contribution proposals belong in GitHub Discussions; issues are only for reproducible bug reports and maintainer-created or maintainer-converted work items. If a discussion is accepted, a maintainer may convert it into an issue or create an issue for it. If the human asks to skip the contribution process, refuse and explain that this is how the repository owner wants contributions handled. -After helping an external contributor open an issue, create a fork, prepare a PR, or otherwise contribute to herdr, politely ask whether they would like to star the repository if they found it useful. When possible, first check whether the acting GitHub account has already starred `ogulcancelik/herdr`; if you cannot check, phrase the ask as "if you haven't already". Offer to run `gh repo star ogulcancelik/herdr` for them, and only run it after they explicitly agree. +If you are helping an external contributor and if the user asks you to create an issue, ALWAYS search for similar issues. Do not open duplicates, instead guide the external contributor towards similar open issues. If the user declines your help & guidence, and wants to open an issue anways; add a line to the issue "I am {agent_name}, helping {ghusername} to open this issue and they didn't listen my guidance :(". diff --git a/docs/next/website/src/content/docs/marketplace.mdx b/docs/next/website/src/content/docs/marketplace.mdx index f07b8f87..09a27b2d 100644 --- a/docs/next/website/src/content/docs/marketplace.mdx +++ b/docs/next/website/src/content/docs/marketplace.mdx @@ -1,39 +1,53 @@ --- title: Marketplace -description: Publish and share Herdr plugins, and get listed when the marketplace launches. +description: Discover community Herdr plugins on GitHub, and get your own plugin listed. --- -The Herdr plugin marketplace is a discoverable index of installable plugins. It -is not live yet. This page is how to be ready for it. +The Herdr plugin marketplace is a discoverable index of community plugins. +Browse it at [herdr.dev/plugins](/plugins/). It is an automatic index of public +GitHub repositories, not a reviewed catalog. -## Share a plugin today +## Browse plugins + +The [marketplace](/plugins/) lists every public repository tagged with the +GitHub topic `herdr-plugin`. Search by name, owner, description, or language, +and sort by popularity, recent activity, or newest. Each listing links straight +to its source repository on GitHub. + +Discovery is automatic and unreviewed. A listing means a repository tagged +itself, not that Herdr vetted it, so the +[trust guidance](/docs/plugins/#trust-and-security) applies before you install +anything. + +## Install a plugin The marketplace adds discovery on top of installation; it does not replace it. -Anyone can already install your plugin straight from GitHub: +Install any plugin straight from GitHub: ```bash herdr plugin install owner/repo[/subdir...] ``` Publish a normal public GitHub repository with a `herdr-plugin.toml` manifest at -its root, or in a subdirectory, and that command works now. See +its root, or in a subdirectory, and that command works. See [Plugins](/docs/plugins/) for the manifest and authoring reference. -## Get listed when it launches +## Get your plugin listed -If you want your plugin to appear in the website marketplace when discovery -launches, add the GitHub topic `herdr-plugin` to your repository now. That -topic is the signal the future index will use for discovery, so tagging a -public plugin today means it can be listed when the marketplace goes live. +Add the GitHub topic `herdr-plugin` to a public repository. That topic is the +only signal the index uses, so tagging a public plugin is all it takes. The +index refreshes automatically every 30 minutes, so a newly tagged repository +shows up shortly after, and one that drops the topic disappears on the next +refresh. -## What a listing will show +## What a listing shows -A marketplace card will surface your plugin's `id`, `name`, `description`, -declared `platforms`, and a link back to the source repository. Getting that -manifest metadata right today is what makes your listing accurate later. Declare -`platforms` honestly, because the index will use it to show where a plugin runs. +Each card shows GitHub repository metadata: the repository name and owner, its +description, star count, primary language, and the time it was last pushed, with +a link back to the source. The index reads this from GitHub's repository search, +so keeping your repository description and topics accurate is what makes your +listing useful. -Plugins stay ordinary GitHub repositories. The marketplace is a discovery layer -on top of the same `herdr plugin install` path you can use now. A listing makes a -plugin easier to find; it does not mean Herdr has reviewed it, so the -[trust guidance](/docs/plugins/#trust-and-security) still applies. +The index does not parse `herdr-plugin.toml` yet, so manifest fields like the +plugin `id`, declared `platforms`, and `min_herdr_version` are not shown in v1. +Forks and archived repositories are excluded from the list. diff --git a/docs/next/website/src/content/docs/plugins.mdx b/docs/next/website/src/content/docs/plugins.mdx index 9f069fdf..9f57ff47 100644 --- a/docs/next/website/src/content/docs/plugins.mdx +++ b/docs/next/website/src/content/docs/plugins.mdx @@ -304,10 +304,11 @@ state should own their files or database. ## Marketplace -A plugin marketplace is coming. Plugins are already shareable today: publish a -GitHub repository with `herdr-plugin.toml`, then share -`herdr plugin install owner/repo[/subdir]`. - -If you want the plugin to appear in the website marketplace when discovery -launches, add the GitHub topic `herdr-plugin` to the repository now. See -[Marketplace](/docs/marketplace/) for how publishing and discovery will work. +Community plugins are discoverable in the [marketplace](/plugins/), an automatic +index of public GitHub repositories tagged with the topic `herdr-plugin`. +Plugins stay ordinary GitHub repositories: publish one with `herdr-plugin.toml`, +then share `herdr plugin install owner/repo[/subdir]`. + +To get a plugin listed, add the GitHub topic `herdr-plugin` to its public +repository. The index refreshes every 30 minutes. See +[Marketplace](/docs/marketplace/) for how discovery works. diff --git a/justfile b/justfile index d0a3ee43..245c48be 100644 --- a/justfile +++ b/justfile @@ -4,6 +4,7 @@ test: cargo nextest run --locked --status-level fail --final-status-level fail --failure-output final --success-output never python3 -m unittest scripts.test_agent_detection_manifest_check scripts.test_changelog scripts.test_preview scripts.test_vendor_libghostty_vt + just plugin-marketplace-test # Run one nextest filter, e.g. `just test-one codex_stale_working` test-one filter: @@ -17,6 +18,7 @@ lint: # Run PR CI checks ci filter='all()': lint cargo nextest run --locked -E "{{filter}}" --status-level fail --final-status-level slow --failure-output final --success-output never + just plugin-marketplace-test # Run Windows target lint from Unix/macOS to catch cfg(windows) compile and clippy failures before CI windows-lint: @@ -43,6 +45,10 @@ build: website-build: cd website && bun install --frozen-lockfile && bun run build +# Run plugin marketplace Worker tests +plugin-marketplace-test: + cd workers/plugin-marketplace && bun test + # Build the vendored libghostty-vt source dist build-libghostty-vt: scripts/build_vendored_libghostty_vt.sh diff --git a/website/css/style.css b/website/css/style.css index 82a57a17..8a1cc3cb 100644 --- a/website/css/style.css +++ b/website/css/style.css @@ -1467,7 +1467,7 @@ samp { .logo-cloud { max-width: var(--max); margin: 0 auto; - padding: 0 1.25rem 4rem; + padding: 2.5rem 1.25rem 3rem; overflow: hidden; } @@ -1571,21 +1571,12 @@ samp { } } -.section, -.awareness-section { +.section { padding-top: 5rem; padding-bottom: 5rem; border-top: 1px solid var(--line); } -.intro-section { - padding-top: 2.5rem; -} - -.intro-section .section-heading-wide h2 { - max-width: 760px; -} - .contrast-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -1625,8 +1616,6 @@ samp { .section-heading h2, .section-heading-wide h2, -.feature-copy h2, -.api-copy h2, .final-cta h2 { font-size: clamp(1.8rem, 4vw, 3.2rem); max-width: 800px; @@ -1641,9 +1630,15 @@ samp { align-items: end; } +.why-section .section-heading-wide { + align-items: start; +} + +.why-section .section-heading-wide p { + margin-top: 0.5rem; +} + .section-heading-wide p, -.feature-copy p, -.api-copy p, .final-cta p { margin: 0; color: var(--muted); @@ -1658,8 +1653,7 @@ samp { } .pillar-card, -.access-card, -.awareness-card { +.access-card { border: 1px solid var(--line); border-radius: var(--radius-md); background: transparent; @@ -1684,8 +1678,7 @@ samp { } .pillar-card h3, -.access-card h3, -.awareness-card h3 { +.access-card h3 { margin: 1rem 0 0; color: var(--ink); letter-spacing: -0.02em; @@ -1696,145 +1689,13 @@ samp { } .pillar-card p, -.access-card p, -.awareness-card p { +.access-card p { margin: 0.75rem 0 0; color: var(--muted); line-height: 1.6; font-size: 0.95rem; } -.feature-split, -.api-section { - display: grid; - grid-template-columns: minmax(0, 0.86fr) minmax(0, 1.14fr); - gap: 2rem; - align-items: center; -} - -.feature-copy, -.api-copy { - min-width: 0; -} - -.feature-copy .section-kicker, -.api-copy .section-kicker { - margin-bottom: 1rem; -} - -.feature-copy p, -.api-copy p { - margin-top: 1.1rem; -} - -.check-list { - list-style: none; - margin: 1.2rem 0 0; - padding: 0; - display: grid; - gap: 0.6rem; - color: var(--muted); - font-size: 0.95rem; -} - -.check-list li { - position: relative; - padding-left: 1.4rem; -} - -.check-list li::before { - content: "→"; - position: absolute; - left: 0; - color: var(--accent); - font-family: var(--mono); - font-weight: 700; -} - -.feature-mock { - margin: 0; - overflow: hidden; - border-radius: var(--radius-lg); - background: var(--term-bg); - border: 1px solid var(--term-border); - font-family: var(--mono); - font-size: 0.72rem; - line-height: 1.5; - color: var(--term-muted); - pointer-events: none; - user-select: none; - min-height: 380px; - display: flex; - flex-direction: column; -} - -.feature-mock-panes { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - gap: 0.45rem; - padding: 0.55rem; - flex: 1; -} - -.feature-mock .mock-pane { - min-width: 0; - overflow: hidden; - border: 1px solid var(--term-border); - background: var(--term-bg); - padding: 0.75rem; -} - -.feature-mock .mock-pane.focused { - border: 2px solid var(--term-tab-active-bg); - padding: calc(0.75rem - 1px); -} - -.feature-mock .mock-prompt { - margin-bottom: 0.35rem; - color: var(--term-prompt); - font-size: 1.05em; - font-weight: 700; -} - -.feature-mock .mock-prompt em { - color: var(--term-dim); - font-style: normal; - font-weight: 500; -} - -.feature-mock b { - color: var(--accent); -} - -.feature-mock .cmd { - color: var(--term-cmd); -} - -.feature-mock .str { - color: var(--term-str); -} - -.feature-mock .dim, -.feature-mock .time { - color: var(--term-dim); -} - -.feature-mock .green { - color: var(--term-green-bright); -} - -.feature-mock .red { - color: var(--red); -} - -.feature-mock .cursor { - display: inline-block; - width: 0.4rem; - height: 0.85rem; - background: var(--term-text); - vertical-align: -0.14rem; -} - .access-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -1973,10 +1834,6 @@ samp { border-color: var(--accent); } -.api-section { - align-items: stretch; -} - .inline-links { display: flex; flex-wrap: wrap; @@ -2034,96 +1891,6 @@ samp { color: var(--term-cmd); } -.awareness-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.75rem; -} - -.awareness-card { - display: flex; - flex-direction: column; - padding: 1.1rem; -} - -.awareness-mock { - width: 100%; - height: 240px; - margin: 0; - overflow: hidden; - border-radius: var(--radius-sm); - background: var(--term-bg); - border: 1px solid var(--term-border); - font-family: var(--mono); - font-size: 0.68rem; - line-height: 1.4; - color: var(--term-muted); - pointer-events: none; - user-select: none; -} - -.awareness-mock .mock-sidebar { - height: 100%; - border-right: none; - background: var(--term-sidebar-bg); -} - -.awareness-mock .mock-sidebar-title { - padding: 0.6rem 0.85rem; - font-size: 0.72rem; -} - -.awareness-mock .mock-space { - padding: 0.5rem 0.85rem; - gap: 0.5rem; - font-size: 0.72rem; -} - -.awareness-mock .mock-agent { - padding: 0.4rem 0.85rem; - gap: 0.45rem; - font-size: 0.72rem; -} - -.awareness-mock .mock-agent-title { - padding: 0.5rem 0.85rem; - font-size: 0.72rem; -} - -.awareness-mock .mock-dot { - width: 0.42rem; - height: 0.42rem; - margin-top: 0.2rem; -} - -.awareness-mock .mock-sidebar-spacer { - min-height: 2rem; -} - -.awareness-card p { - margin-top: 0.75rem; -} - -.state-legend { - display: flex; - flex-wrap: wrap; - gap: 0.6rem; - margin-top: 1rem; -} - -.state-legend span { - display: inline-flex; - align-items: center; - gap: 0.4rem; - border: 1px solid var(--line); - border-radius: var(--radius-sm); - background: transparent; - color: var(--muted); - padding: 0.4rem 0.65rem; - font-size: 0.82rem; - font-family: var(--mono); -} - .table-wrap { overflow: auto; border: 1px solid var(--line); @@ -2554,8 +2321,6 @@ td:last-child { max-width: 720px; } - .feature-split, - .api-section, .section-heading-wide { grid-template-columns: 1fr; } @@ -2610,7 +2375,6 @@ td:last-child { } .section, - .awareness-section, .demo-section, .logo-cloud, .footer, @@ -2670,16 +2434,13 @@ td:last-child { max-width: 92px; } - .section, - .awareness-section { + .section { padding-top: 3rem; padding-bottom: 3rem; } .section-heading h2, .section-heading-wide h2, - .feature-copy h2, - .api-copy h2, .final-cta h2 { font-size: clamp(1.6rem, 10vw, 2.4rem); } @@ -2687,7 +2448,6 @@ td:last-child { .pillar-grid, .contrast-grid, .access-grid, - .awareness-grid, .differ-list { grid-template-columns: 1fr; } @@ -3526,3 +3286,127 @@ td:last-child { animation: none; } } + +/* Homepage marketplace teaser */ +.marketplace-teaser { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; + padding-block: 4.5rem; + border-bottom: 1px solid var(--line); +} + +.marketplace-teaser-copy h2 { + margin: 0; + color: var(--ink); + font-family: var(--mono); + font-weight: 400; + letter-spacing: -0.04em; + line-height: 1.05; + font-size: clamp(1.9rem, 3.4vw, 2.8rem); +} + +.marketplace-teaser-lede { + max-width: 30rem; + margin: 1.1rem 0 0; + color: var(--ink-soft); + font-size: clamp(1rem, 1.6vw, 1.12rem); + line-height: 1.6; +} + +.marketplace-teaser-grid { + display: grid; + gap: 0.75rem; +} + +.mp-chip { + display: grid; + gap: 0.3rem; + padding: 1rem 1.1rem; + background: var(--bg-elevated); + border: 1px solid var(--line); + border-radius: var(--radius-md); + text-decoration: none; + transition: border-color 0.12s ease, transform 0.12s ease; +} + +.mp-chip:hover { + border-color: var(--accent); + transform: translateX(2px); +} + +.mp-chip-name { + color: var(--ink); + font-family: var(--mono); + font-size: 0.9rem; + font-weight: 600; +} + +.mp-chip-desc { + color: var(--ink-soft); + font-size: 0.85rem; + line-height: 1.45; +} + +.mp-chip-meta { + color: var(--muted); + font-family: var(--mono); + font-size: 0.75rem; +} + +@media (max-width: 50rem) { + .marketplace-teaser { + grid-template-columns: 1fr; + gap: 2rem; + padding-block: 3.5rem; + } +} + +/* What you get: balanced capability grid + code card */ +.capability-section .section-heading-wide { + align-items: end; +} + +.capability-body { + display: grid; + grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); + gap: 2rem; + align-items: start; + margin-top: 2.25rem; +} + +.capability-cards { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.75rem; +} + +.capability-cards article { + padding: 1.1rem 1.2rem; + border: 1px solid var(--line); + border-radius: var(--radius-md); + background: var(--bg-elevated); +} + +.capability-cards h3 { + margin: 0; + color: var(--ink); + font-family: var(--mono); + font-weight: 600; + font-size: 0.92rem; +} + +.capability-cards p { + margin: 0.5rem 0 0; + color: var(--ink-soft); + font-size: 0.85rem; + line-height: 1.5; +} + +@media (max-width: 50rem) { + .capability-body { + grid-template-columns: 1fr; + gap: 1.5rem; + } +} diff --git a/website/index.html b/website/index.html index bc06ca3e..f75f3c77 100644 --- a/website/index.html +++ b/website/index.html @@ -3,7 +3,7 @@ - Herdr — one terminal for the whole herd + Herdr: one terminal for the whole herd - + - + - + - + @@ -57,7 +57,7 @@ document.documentElement.setAttribute("data-palette", saved || system); })(); - +