The homepage, README, sponsor copy and comparison pages now say plainly
what Paseo is: a one-click desktop app with the daemon bundled, a native
mobile app with full parity, parallel agents in worktrees, and plugins
that add panels and functionality on desktop, web and mobile.
Every comparison page uses one table with the same rows so readers can
compare products row by row. The product columns use a fixed layout so
column widths no longer follow the longest cell.
Sponsor copy opens with the project's size before the one-person fact,
so a reader does not take "one person" to mean a side project.
The directory header now keeps only "Submit a plugin", next to the search
box. Building and submitting move into a "Build your own plugin" section at
the end of the page, with "Read the docs" and "Submit a plugin" buttons.
The docs link is labelled "Read the docs" because that is what it does.
The site footer gets top margin on every page; it sat too close to content.
* website: plugin page design mockups (four directions, throwaway routes)
* website: final plugin page mockup (A directory, B browse frame, list cards, D detail)
* website: browse pages use a four-column card grid in the final plugin mockup
* feat(plugins): keep registry installs behind a daemon flag, off by default
Off restores the pre-registry behavior: bare owner/repo is GitHub shorthand and the
daemon never resolves or contacts a registry on install, update preview, or update.
Enable with pluginRegistryEnabled: true or PASEO_PLUGIN_REGISTRY_ENABLED=1.
* feat(website): ship the plugin directory, browse pages, and single-column detail
Directory: count, build/submit links, What's new, the nine categories, and Most installed
with a This week / This month / All time switch. Browse pages at /plugins/all and
/plugins/category/<slug> with a category sidebar, tabs, and a four-column grid; old
/plugins?category= links redirect. Install counts gain daily KV buckets kept 60 days.
* fix(website): keep a README's leading GitHub alert on the plugin page
* feat(website): search plugins from the directory and browse pages
Typing replaces the URL with /plugins/all?q=<term> (or the current category
with ?q=), which renders matching plugins on the server. Matching covers
name, description, ID, and author. Old /plugins?q= links redirect.
* fix(website): keep the directory's ranking window when searching
* feat(plugins): install reviewed artifacts from open registries
* fix(website): build protocol before deployment checks
* fix(website): make plugin pages discoverable to search engines
Resolve missing registry entries in route loaders to return HTTP 404. Serve a registry-backed sitemap so newly listed plugins and authors are discoverable without rebuilding the website.
* feat(website): keep plugin directory unlinked for coordinated launch
Preserve direct routes and SEO while retaining the partner directory link. Cover nested Git artifacts through registry installation and pinned website commands.
* fix(website): link plugin cards by registry owner and stub the clipboard in e2e
Plugin and author URLs come from the owner segment of the plugin ID, not the author's GitHub login, so a listed plugin always resolves. The Playwright spec records clipboard writes instead of granting a permission WebKit does not have.
* feat(app): unify Explorer tabs and refine interface styling
Give Explorer the workspace tab controls while keeping launcher choices separate from drag-and-drop support.
Upgrade Lucide and set a shared icon stroke width for consistent styling. Reduce the desktop composer bottom inset to align the action row with its corners.
* fix(app): show active Explorer tabs in foreground
The shared row coupled foreground styling to keyboard shortcut ownership. Separate those concerns so Explorer can highlight its active tab while Cmd+T remains with the workspace pane.
* fix(app): refine Explorer close controls and launch options
* test(app): respect singleton Files in Explorer plugin workflow
* test(server): trim unused setup from no-track checkout regression
* test(app): count visible main tab rows after pane moves
Swap Inter for Geist as the website sans font. FAQ +/− toggles now sit on
the content rail instead of hanging outside it, slightly larger, with
answers indented to the question text.
The website dev server crashed with "createStartHandler is not a function"
on every SSR hot reload. Vite 7's module runner races when re-evaluating
the static import chain behind TanStack Start's server handler
(TanStack/router#7285, fixed upstream in vitejs/vite#22369). The worker
entry now imports the handler at request time until the website moves to
a Vite release with that fix.
* feat(website): feature OMP, add agent icons to /agents, and use Inter
Featured agents on the homepage are now Claude Code, Codex, OpenCode, Pi,
OMP, Cursor, Muse Code, and Antigravity, driven by one list so the hero
badges, the provider cards, and the "+N more" count stay in sync. The count
previously subtracted a stale constant (5) while 7 agents were shown.
/agents moves onto the shared SiteShell like Hub and Changelog and lists
every agent as an icon and name. Icons for ACP agents are vendored from the
app's provider catalog rather than imported across packages.
The site switches to self-hosted Inter so typography no longer depends on
the visitor's OS; system-ui fell back to DejaVu Sans on many Linux setups.
* fix(website): keep vendored agent marks out of the homepage bundle
The homepage only shows the eight featured agents, which have inline icon
components, but importing AgentPageIcon pulled all 31 vendored SVGs into
its bundle. AgentPageIcon now lives in its own module used only by /agents.
The Codebuddy mark drew past its 16x16 viewBox and was clipped; the
viewBox now matches the artwork's bounds.
* feat(website): add a thanks page after downloading
Desktop and APK download buttons on /download and the homepage now open
/download/thanks, which starts the download and points people to Discord,
GitHub, Reddit, and sponsoring while it runs. The button carries the asset
name rather than the file URL, so the page resolves the URL from the
release data itself and cannot be pointed at an arbitrary link.
* fix(website): start the exact file the visitor clicked on the thanks page
The thanks page looked the file up again from an asset key and channel, so
it could start a different release than the button showed (a release
published in between, or a beta link without a beta). Download links now
carry the release file URL itself; the thanks page accepts only URLs under
this repo's GitHub releases, after URL parsing normalizes `..` segments.
The e2e test waits for the file request instead of a download event,
because WebKit reports no download event for a stubbed response.
* test(website): assert the thanks-page download event outside WebKit
* test(website): split the thanks-page download check out of the shared test
* Revert "Remove the unreleased Antigravity provider plugin (#5781)"
This reverts commit 9d9d9af993.
* fix(providers): commit plugin notices emitted during session opening
* feat(providers): restore Antigravity with full access and session warnings
* fix(providers): warn once when opening Antigravity sessions
Driver respawn retains the live transcript and unchanged permission posture. Emit the warning only when opening a session, including resume after a daemon restart.
* refactor(providers): remove the unreleased Antigravity plugin
Remove the provider at the owner’s request because headless command approvals are unsupported. Preserve Muse, shared plugin infrastructure, and earlier editor and terminal integrations.
* test(plugins): keep builtin startup coverage generic
* test(muse): record real host protocol fixtures
* feat(muse): add built-in MSP provider
Co-authored-by: millerben95 <millerben95@users.noreply.github.com>
* fix(muse): expose provider settings in the app
The daemon registered host settings without a client contribution, leaving sandbox, network, and workspace trust unreachable. Share the definition with a settings screen and expose it through Command Center even when installed plugins are disabled.
* docs(muse): publish setup and provider support
* test: correct runtime fixtures and restart readiness
* fix(muse): hide reminder housekeeping from the timeline
* fix(muse): use the official Muse Code mark
Source: https://muse.ai/images/favicon/app-squiggle.svg
Preserve the official silhouette path verbatim. Remove its gradient definition and use currentColor, scaling the original 100-unit coordinates into the 24-unit viewBox. Black silhouette renders match pixel-for-pixel.
* fix(muse): adapt to current plugin APIs
Use the screen API introduced with sidebar plugin items. Usage reports now come from source account discovery; remove the retired agent usage-reference request and headline marker, and exercise discovery through the existing provider fixtures.
---------
Co-authored-by: millerben95 <millerben95@users.noreply.github.com>
* feat(website): present company sponsorship as support, not ad space
The homepage showed four empty "Sponsor / $500 a month" boxes under a
"New" badge, right below a note about staying independent. They read as
ad inventory for sale. The price now lives only on /sponsor, next to the
checkout. The homepage shows a Sponsors row only once a company has
sponsored, and the READMEs link to /sponsor instead of straight to
Stripe checkout.
* feat(website): drop subtitles from the sponsor options
Each option carried a line like "From $5 a month" or "with an invoice" that tried to set it apart, but the differences are not real: GitHub Sponsors invoices too and any channel takes small amounts. Showing the names lets people pick the platform they already use.
* feat(website): rework sponsorship with homepage sponsor spots
The founder note now states how the project is funded (sponsorship and
Paseo Hub) instead of describing the product, and the same note and
backing links render on the homepage and /sponsor from one module so the
two cannot drift.
Companies can sponsor one of four homepage logo spots at $500 a month
through Stripe rather than GitHub Sponsors, because they need invoices
and a vendor relationship. The spots render as placeholders until sold;
approving a sponsor is an entry in src/data/sponsors.ts. The checkout
URL is a placeholder until the Stripe Payment Link exists.
* feat(website): point sponsor spot checkout at the Stripe payment link
* docs(readme): add a sponsors section
* docs(readme): add the sponsors section to the translated READMEs
* docs(website): document the sponsor approval and Stripe cap workflow
* fix(website): render phone mockups correctly in Safari
Replace trigonometric CSS scaling with measured numeric ratios after Safari placed phone content outside its clipped frames. Scale desktop mockup corners with the frame so they remain proportional on mobile.
* test(website): expose the website browser test suite
* test(website): run mockup regressions in CI
Add a paseo.cafe link to the Plugins card on the landing page and a
section at the top of the related projects doc. The card link is accented
so it reads without hover; the card now takes a list of links instead of
one hardcoded link.
The homepage download CTA was chosen by a client-only hook that started at
"mac-silicon" and corrected itself in an effect, so the server-rendered HTML
said "Download for Mac" for every visitor and only became right after
hydration. On a phone that window is long enough to photograph. Worse, the
detection had no mobile branch at all: Android user agents also say "Linux",
so once hydration did land, a Pixel was offered an AppImage.
Resolve the platform from the request user agent during SSR instead, so the
first paint is already correct. Mobile visitors get the App Store or Play
Store as the primary action and a link to /download for the desktop build,
rather than a .dmg they cannot open.
The markup now varies by user agent, so HTML and server-function responses
carry `Cache-Control: private, no-store` and `Vary: User-Agent`. An absent
cache-control is not no-store — a shared cache may assign heuristic freshness,
and turning on Workers Cache would store a headerless 200 for two hours under
a key that ignores the user agent, letting one Android visitor pin the Play
Store button for every Mac visitor after them.
Also tighten the mobile layout the wrong CTA was sitting in: the primary
action gets its own row so the icon buttons stop orphaning one of themselves,
provider cards drop to a two-column grid, and the hardcoded mobile line break
in the phone showcase gives way to a balanced measure.
* refactor(storage): remove chat rooms and agent loops
This is the prerequisite cleanup for the storage backend migration. Legacy wire schemas remain temporarily so mixed-version peers can still parse old messages, while daemon callers now use query-shaped AgentStorage methods that can map directly to SQL later.
* test(cli): remove retired chat and loop coverage
* docs(hub): name the Hub tools in workflow prompts
Every Hub workflow example now tells the agent which tool to call:
hub.reply for the user-facing response, hub.finish_execution to
complete the step and record structured output. Granting a capability
through allow_outputs makes the tool available; it does not make an
agent use it, and an agent that is not told answers in its own
transcript and leaves the step to its idle timeout. Explicit
instructions are what worked against real agents.
Classifier and other intermediate steps name hub.finish_execution
alone, so the docs do not imply every step must reply.
Drops the github.reply allow_outputs blocks from the GitHub examples.
The GitHub provider registers no output capabilities, so that
declaration never materializes a tool; those agents comment through
their scoped GH_TOKEN with gh, which the reference and trigger pages
already said. The read-only answer branch has no network authority for
that, so it only finishes.
Also quotes the "Request: ${{ paseo.prompt }}" prompt block in the
reference, which was invalid YAML as written.
* docs(hub): wrap triggering input in its own prompt block
Every Hub YAML example now ends its prompt with the raw triggering
message wrapped in <user-prompt> tags, as a separate block after the
workflow instructions. The agent can see where your instructions stop
and the request starts, which matters most on the steps that read
external Slack, Discord, and GitHub text.
The first workflow explains the convention in two sentences and points
at Hub security for the controls that actually limit an agent. Tags
label the input; they are not containment, and the docs do not claim
otherwise.
Replaces the earlier quoting fix in the prompt-blocks reference: the
message now lives in a block scalar, so the colon needs no quoting.
* docs: base worktree examples on origin/main
Every branch-off example now passes a remote-tracking ref. Paseo
fetches remote refs in the background, so origin/main is current while
the local main is whatever was last pulled, and base resolution tries
refs/heads first: an unqualified main silently branches off stale
history. The gotcha is explained once, next to the first branch-off
example in the worktrees guide; the other pages just use the ref, and
the Hub environment reference links there.
Also points the safety-gate classifier at the structured-output
section, which shows the output wrapper the finish call takes.
* docs(hub): configure Codex read-only through provider options
Hub examples described a read-only Codex step as mode: read-only. That
Paseo mode preset pairs the read-only sandbox with approval_policy
on-request, so an unattended Hub step stops to ask a question nobody is
there to answer. It is also not one of the modes the Codex provider
advertises.
Every read-only step now uses the provider-native passthrough proven by
the real-provider evidence in #3025: approval_policy never,
sandbox_mode read-only, web_search disabled. The security guide already
used this block; the workflow guide, examples, and reference now agree
with it, and the reference says plainly that a Paseo mode ID is a
separate surface from these options.
mode: full-access is unchanged. It maps to approval_policy never, so it
does not stall an unattended step.
* docs(hub): show a classifier choosing the repository and the model
Both routing jobs were only documented as caller-supplied inputs, so
there was no example of the thing people actually want: one read-only
classifier that reads the request, picks the repository it belongs to
and the model it needs, and hands both to a worker. Replaces the
caller-only model example, which the new one covers through its
override values.
The two axes are not symmetric and the example says so. A model is a
value, so values.selected_model goes straight into agent.model. An
environment cannot come from an expression at all: the compiler accepts
a literal name or paseo.inputs.<name> and nothing else, so each
repository needs its own environment and its own conditional step. One
classifier, exactly one worker.
Corrects the routing section, which listed environment among the fields
an agent output may select. It never could.
Also puts a blank line between adjacent steps and environments in the
deadline and repository-routing examples, and bases the daemon worktree
example on origin/main, which the earlier sweep missed.
* fix(website): keep development requests local
Canonical host redirects were applied to LAN-addressed development traffic, including inspector WebSockets. The redirected socket retained the Vite port and caused the development proxy to terminate when the public host timed out.
* docs(hub): order the workflow scenarios from simple to hard
The Hub pages were arranged by the shape of the configuration schema
rather than by what a reader is trying to do, which showed up three
ways: the overview opened on the storage hierarchy, workflow
configurations were split between workflows.md and a separate examples
page, and the examples that existed started with the hardest one.
workflows.md is now a ladder. One Slack trigger answering in a thread
stays inline as the first workflow, and nine further scenarios sit
under it as collapsible blocks, each a complete configuration adding
one idea to the one above: full access, repeated replies, a GitHub
trigger, two ordered steps, a typed input, two triggers, a classifier
choosing a model, a classifier choosing a repository, and last the
override composed with ??. configuration/examples.md is removed and its
configurations live there now.
Two claims in the old material were wrong and are corrected. Nothing
carries between steps except a declared output, so the planning
scenario passes its plan through output.schema instead of telling the
second agent to follow a plan it cannot see, and a worktree environment
creates a worktree per execution rather than sharing one. GH_TOKEN
authenticates the gh CLI but does not configure git, so the pages no
longer imply a step can push without a commit identity and credential
helper on the daemon host.
The overview drops the organization/connections/daemons/projects tree,
which concepts.md already owns, in favour of the smallest configuration
that does something. Hosting is now a link from the product pages
rather than a section in them.
* docs(hub): make GitHub access an explicit step grant
GitHub authority gets its own page. A step holds a token only when it
declares a github block — connection, repositories, permissions,
duration — and Hub supplies GH_TOKEN plus environment-only git
configuration, revoked when the step ends. No credential is ever
implied by the trigger source.
Rewrites the issue-to-pull-request scenario onto the block, repoints
the trigger, reference, FAQ, and concepts pages, and sweeps the Hub
pages for announcing sentences and compressed paragraphs.
* docs(hub): lighten dense reference prose
Restructure paragraph walls into short paragraphs, lists, and code
blocks: logout behavior in daemons, deploy paths and precedence in
configuration, install limits and dispatch fields in the API page,
required outputs and partial resolution in the reference, and the
policy translation and secrets guidance in security. Fixes two
remaining trigger-derived credential claims in triggers/index.
* fix(nix): update npm dependency hash
* docs(hub): document shared prompts and authority defaults
* feat(website): nest docs pages under landable groups
The docs sidebar could already render groups from subdirectories in
public-docs, but nothing used it because every doc was a flat file, and
two things stopped it being usable.
A group had no page of its own, so its label was only a toggle and its
name came from the directory name via formatLabel. A directory's
index.md now supplies the group's label, order, and href, which makes
the group landable and lets the label be authored rather than derived.
Categories and top-level directories named the same thing, so a
categorised doc at hub/index.md produced a group nested inside its own
category. Only directories below the top level become groups now.
Also fixes two behaviours in the sidebar:
- The open/closed effect assigned containsActive on every change, so a
group you expanded to browse collapsed the moment you navigated
anywhere else. Navigating in now opens a group and navigating away
leaves it alone.
- The sidebar scrolls independently of the page and never scrolled the
active entry into view, so loading or refreshing a page far down the
list left the sidebar parked at the top with no visible position.
The group row is a single target: it navigates and expands, and
collapses when clicked again from the page it points at. A separate
chevron button would put two actions a few pixels apart.
* docs(hub): document Hub
Adds a Hub section covering what it is, how it works, daemons,
triggers, configuration, activity, and the two ways to run it.
The section separates using Hub from running it. Overview, quickstart,
daemons, triggers, and configuration hold regardless of who operates
the deployment; the self-hosting branch owns deploying the service and
creating the GitHub, Slack, and Discord apps it talks to. That split
falls on a real seam, because those apps are exactly what a hosted Hub
would own on your behalf. Hosted is a placeholder until it ships.
Each provider gets a trigger page carrying its events, filter
semantics, and the full paseo.event.* reference for the data a prompt
can read. That data is the thing a config author actually needs and
the thing they cannot get from anywhere else.
Every YAML block was parsed against the Hub config schema rather than
eyeballed, which kept several plausible-looking things out of the
docs: worktree prNumber takes a literal number and not a merge
variable, only daemon environments run today, and github.push has no
actor so it can never satisfy the required from_users filter.
Troubleshooting moves to order 90 so it stays last.
* refactor(website): let the active docs entry scroll itself into view
The layout queried the sidebar for the active entry after every
navigation, which coupled it to when that entry mounts. A link inside a
collapsed group only mounts once the group's own effect opens it, so
the query could run against a DOM that does not contain it yet.
Moving the scroll to the entry removes the ordering question rather
than answering it, and drops the ref and the cross-component query.
Reported by Greptile on #2864. The failure it described does not
reproduce: navigating from a page outside a collapsed group into one of
its children still scrolls correctly, verified with the group confirmed
collapsed and the sidebar forced to the top. The coupling was real even
though the bug was not.
* docs(hub): stop listing BETTER_AUTH_URL as required
It falls back to PASEO_CLOUD_PUBLIC_URL, so a deployment that sets the
public origin already has a correct auth base URL. Listing it as
required made the Fly recipe look incomplete when it was not.
Moved to optional with its default, and recorded what actually breaks
without BETTER_AUTH_SECRET: the auth routes close and nobody can sign
in.
Reported by Codex on #2864, which read the same inconsistency the other
way round and proposed adding the variable to the Fly example.
* docs(hub): attach a database in the Fly recipe
The Fly section showed inline config and a secrets command, neither of
which supplied DATABASE_URL. It defaults to localhost:5432, which on
Fly reaches nothing, so the documented deployment could not start.
fly postgres attach sets DATABASE_URL as a secret, so the step is the
attach itself rather than another variable to copy.
Reported by Codex on #2864.
* docs: align Hub self-hosting contract
The Cloud page pitched a hosted multi-user product and collected emails
through a signup form. The Hub is a self-hosted service in private beta
and the only way in is a Discord DM, so the signup server function and
its webhook are deleted rather than repointed. /cloud now 301s to /hub
from the worker entry, which already owns the site's permanent path
redirects, so old links and search results keep working.
The homepage also drops split panels, service proxy, voice, and keyboard
shortcuts. They had turned the page into a feature list; it should show
what Paseo enables at a high level.
Brand and provider icons move into shared modules so the Hub page, the
site header, and the landing page each use one copy instead of three.
* feat(ui): improve changelog and help navigation
Make release notes easier to scan and reach from the app. Also keep the homepage marquee within the layout viewport so it cannot create horizontal scrolling.
* fix(website): reject duplicate release anchors
* Rework docs navigation and move alternatives to top-level pages
- Add nested docs nav with categories, collapsible groups, breadcrumbs,
and a right-hand page outline with heading slugs.
- Move alternatives out of /docs/alternatives into /alternatives/*
marketing pages with 301 redirects.
- Add github-slugger for heading IDs and require explicit width on
SiteShell so new pages can't accidentally pick the narrow prose layout.
- Remove the best-practices doc from public-docs.
* Make docs content column prose-width
Updates the landing page featured agent badges and multi-provider section to show market leaders: Claude Code, Codex, OpenCode, Pi, and Cursor. Replaces the Copilot and OMP badges, updates FAQ references, and uses the proper Cursor SVG from the app catalog.
Adds an llms.txt at the site root with a static product preamble so
LLMs can answer "what is Paseo" without following any links, plus a
curated index of docs, alternatives, and the 38 agent pages. Doc pages
now expose Copy/View as markdown buttons and a raw .md version at
<path>.md following the llms.txt convention.
Also skips the canonical-host redirect for localhost so the dev server
stops punting every request to production.
Addresses the high-volume issues from the Ahrefs audit:
- Worker entrypoint 301s every request not on https://paseo.sh
(www subdomain and http variants), killing the duplicate-page
errors and the HTTP→HTTPS internal-link notices.
- pageMeta() emits <link rel="canonical"> and og:url per route;
every head() call now passes its canonical path.
- /blog stops rewriting itself to /blog?drafts=false; the search
param only appears when explicitly true.
- Sitemap includes /blog and each post.
- Trim homepage title under SERP truncation, rewrite /agents meta
description under 160 chars, pad short descriptions on
/privacy, /download, /cloud, /changelog, /docs.
- app.paseo.sh gets <meta robots=noindex,nofollow> and a
Disallow-all robots.txt so the SPA shell stops polluting the
crawl with H1-missing / no-OG / low-word-count warnings.
- New /cloud page with signup form (email, name, company, role, message)
posting to a Discord webhook via a TanStack Start server function on
the Cloudflare Worker. Webhook URL read from DISCORD_WEBHOOK_URL secret.
- Extract SiteShell, SiteFooter, FAQItem from landing-page.
- Migrate /privacy, /download, /blog, /changelog, /cloud to SiteShell so
they share header + footer with consistent width.
- Add /cloud to header and footer nav.
- Make /docs sidebar full-width; bump footer text from xs to sm.
- Add Cloudflare Workers types and ignore .dev.vars.