A directory of community-built Paseo plugins. Every listing is generated from the plugin's own repo — authors don't fill out a form, they just point us at their code.
registry/<id>.json → scripts/validate-registry.ts (CI, on PR)
→ scripts/scan-plan.ts (pinned incremental plan)
→ targeted security + enrichment scans
→ ignored data/plugins/*.json + public assets
→ Nitro prerender (.output/public)
registry/*.jsonis the only thing a human writes — a GitHub source plus an optional public npmjs package (see Submitting a plugin).scripts/validate-registry.tsruns on every PR that touches registry inputs. It checks the entry is well-formed, the repo/path exists,paseo-plugin.json.idmatches the registry filename, and any declared npm package publishes the same ID and version. CI detects affected paths, runs the app and/or companion-plugin checks, then reports one aggregateAll checks passedresult. App checks cover formatting, lint, types, tests, and the production build; plugin checks cover formatting, lint, types, and tests. See.github/workflows/ci.ymland.github/workflows/validate.yml.scripts/scan-plan.tsresolves each repository head and npm dist-tag once, compares those pinned identities with the last successfully deployed scan state, and selects only new plugin releases. Git repositories use each plugin path's normalizedpackage.json.versionas the change detector, so unrelated monorepo commits do not move the published plugin commit. npm releases use their exact package, version, and SHA-512 integrity.scripts/scan.tsenriches only promoted candidates and retains the complete prior record when a candidate fails. A lightweight npm-only check runs every 15 minutes; Git release checks continue every six hours. Unchanged scheduled runs stop after planning. Missing or invalid prior state safely causes a full scan.- The build imports the generated
data/plugins.jsonviasrc/lib/plugins-data.ts, prerenders every public page, and emits the catalog's machine interfaces:/llms.txt,/llms-full.txt, per-plugin Markdown at/plugins/<id>.md, OpenAPI at/openapi.json, the full JSON catalog at/api/plugins, and focused JSON records at/api/plugin/<id>.json. .github/workflows/deploy-pages.ymlrestores scan state produced only after a successful Pages deployment, builds and verifies the static site when the plan changes, publishes it, and then records the matching state for the next run. The deployed site has no application server or runtime GitHub API access.
All machine-readable outputs are generated from the same scanned plugin records as the website, so
new and updated registry entries require no additional documentation work. Start with
/llms.txt for the compact index or
/openapi.json for the JSON API contract.
/llms-full.txt contains bounded, catalog-generated facts for bulk ingestion. Repository-provided
README text appears only in the individual Markdown listing, after an explicit trust boundary.
Because production is a static GitHub Pages deployment, canonical plugin pages cannot negotiate on
the HTTP Accept header. Each HTML plugin page advertises an explicit text/markdown alternate at
/plugins/<id>.md; explicit URLs work consistently for agents and ordinary HTTP clients.
Fork it, turn on GitHub Pages (Settings → Pages → Source: GitHub Actions), then run the
Enrich and deploy to GitHub Pages workflow. Nothing needs editing: the workflow asks
actions/configure-pages where the deployment lives and passes that to the scan and the build, so
a fork publishes correct links, canonical URLs and a sitemap for its own
https://<owner>.github.io/<repo>/ — while the canonical site, which has a custom domain, keeps
serving from the root. See VITE_SITE_URL/VITE_BASE_PATH in src/lib/site.ts.
The full walkthrough (with a prefilled "create this file on GitHub" button) lives on the site
itself at /submit. The short version — add one file, registry/<your-plugin-id>.json:
Requirements, checked automatically by CI:
- The registry filename is a lowercase kebab-case plugin ID.
path, when present, is at most 500 characters and uses the shared safe repository-path validation applied by both the site and companion plugin.- Your repo (at
path, if given) contains a validpaseo-plugin.jsonwith the sameid. package, when present, is a public npmjs package name with no version, tag, URL, or registry.- Paseo 0.9 installs declared packages from npm; Paseo 0.8 retains the GitHub source.
- Install commands always use the scanner-derived exact npm version or Git commit; mutable tags and branches are never handed to an install or update action.
The plugin name comes from the registry filename after it is validated against the manifest ID.
Description, version, license, screenshots, and the best-effort limitations excerpt are read
automatically. npm-backed plugins show and rank by last-30-day downloads and exact-version publish
date; Git-only plugins retain their existing star and catalog-listing ordering. When package is
present, its current published version is the catalog version and must match the Git source. A
README.md, LICENSE, and an images/ folder in the repository all make a listing better; none
are required to get in.
Paseo Cafe uses the published npm version when package is declared and otherwise uses the Git
plugin directory's package.json.version. Start at a real semantic version such as 0.1.0, not
the 0.0.0 placeholder, and increment it whenever you publish a plugin update. Missing or invalid
versions remain browsable and installable but report their version as unavailable. The scanner
flags 0.0.0, because updates cannot be detected until the maintainer starts incrementing it.
To offer an unreleased build, publish it under npm's next dist-tag. Cafe shows it as an opt-in
Preview when it points to a semantic version different from latest and its exact tarball passes
the same security scan as the stable release. Stable remains the default, and generated commands
pin the resolved version rather than installing the mutable tag.
Open a PR adding your registry/<id>.json. Once Registry validation and CI pass, it's ready to merge.
bun install
bun run registry:validate # check registry/*.json against live GitHub repos
bun run dev # regenerate the listing, then serve http://localhost:3000bun run build ensures the generated listing exists before producing the static site in
.output/public. Run bun run registry:scan explicitly to refresh it. Other useful scripts:
bun run typecheck, bun run lint, bun run test.
TanStack Start (file-based routes under src/routes/), shadcn/ui (src/components/ui/,
base-ui-flavored — polymorphism uses render, not asChild), Tailwind v4, Zod for both the
registry schema (src/lib/registry-schema.ts) and the generated plugin schema
(src/lib/plugin-schema.ts).
{ "repo": "yourname/your-repo", // GitHub "owner/repo", not a full URL "path": "optional/subpath", // omit if your repo *is* the plugin "package": "@yourname/paseo-plugin", // optional; npmjs only, no version or tag "categories": ["productivity"], // free-form, refined over time "platforms": ["macos"], // only if platform-restricted — omit if it runs anywhere "caveats": ["Requires an OpenAI API key"], // short one-liners worth flagging, up to 6 "submittedBy": "yourname" }