Merge branch 'main' into dev
Surgical merge of 74 commits from main into dev. Key features ported: - Provider visibility gating (appVersion filtering for Pi/Copilot) - Pi agent provider + Copilot re-enabled - Provider-declared features system (Codex fast mode) - Workspace dedup by worktree root (adapted to integer IDs) - Bulk close archiving fix for stored agents - Agent creation timeout increase to 60s - Audio/voice crash fixes (external buffer copies) - Multi-host setup fix - Reload agent tab action Dev architecture preserved: SQLite storage, service proxy, workspace setup dialog, hover cards, removed launcher tabs.
This commit is contained in:
@@ -6,13 +6,14 @@ import {
|
||||
appStoreUrl,
|
||||
playStoreUrl,
|
||||
webAppUrl,
|
||||
downloadOptions,
|
||||
getDownloadOptions,
|
||||
useDetectedPlatform,
|
||||
AppleIcon,
|
||||
AndroidIcon,
|
||||
TerminalIcon,
|
||||
GlobeIcon,
|
||||
} from "~/downloads";
|
||||
import { useRelease } from "~/routes/__root";
|
||||
import { Mic } from "lucide-react";
|
||||
import { HeroMockup } from "~/components/hero-mockup";
|
||||
import { ClaudeIcon } from "~/components/mockup";
|
||||
@@ -297,6 +298,8 @@ function MultiProviderSection() {
|
||||
{ name: "Claude Code", icon: <ClaudeIcon size={28} /> },
|
||||
{ name: "Codex", icon: <CodexIcon className="w-7 h-7" /> },
|
||||
{ name: "OpenCode", icon: <OpenCodeIcon className="w-7 h-7" /> },
|
||||
{ name: "Copilot", icon: <CopilotIcon className="w-7 h-7" /> },
|
||||
{ name: "Pi", icon: <PiIcon className="w-7 h-7" /> },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -305,7 +308,18 @@ function MultiProviderSection() {
|
||||
description="Run multiple providers from a single interface. Paseo runs the native agent harness as you'd normally run it, with your skills, config and MCP servers intact."
|
||||
>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{providers.map((p) => (
|
||||
{providers.slice(0, 3).map((p) => (
|
||||
<div
|
||||
key={p.name}
|
||||
className="flex items-center justify-center gap-3 rounded-xl border border-white/10 bg-white/[0.03] px-5 py-4"
|
||||
>
|
||||
<span className="text-white/80">{p.icon}</span>
|
||||
<span className="font-medium">{p.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 sm:w-2/3">
|
||||
{providers.slice(3).map((p) => (
|
||||
<div
|
||||
key={p.name}
|
||||
className="flex items-center justify-center gap-3 rounded-xl border border-white/10 bg-white/[0.03] px-5 py-4"
|
||||
@@ -819,8 +833,9 @@ function GetStarted() {
|
||||
}
|
||||
|
||||
function DownloadButton() {
|
||||
const { version } = useRelease();
|
||||
const detectedPlatform = useDetectedPlatform();
|
||||
const primary = downloadOptions.find((o) => o.platform === detectedPlatform)!;
|
||||
const primary = getDownloadOptions(version).find((o) => o.platform === detectedPlatform)!;
|
||||
const PrimaryIcon = primary.icon;
|
||||
|
||||
return (
|
||||
@@ -906,6 +921,42 @@ function OpenCodeIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
);
|
||||
}
|
||||
|
||||
function CopilotIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 416"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M181.33 266.143c0-11.497 9.32-20.818 20.818-20.818 11.498 0 20.819 9.321 20.819 20.818v38.373c0 11.497-9.321 20.818-20.819 20.818-11.497 0-20.818-9.32-20.818-20.818v-38.373zM308.807 245.325c-11.477 0-20.798 9.321-20.798 20.818v38.373c0 11.497 9.32 20.818 20.798 20.818 11.497 0 20.818-9.32 20.818-20.818v-38.373c0-11.497-9.32-20.818-20.818-20.818z"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
<path d="M512.002 246.393v57.384c-.02 7.411-3.696 14.638-9.67 19.011C431.767 374.444 344.695 416 256 416c-98.138 0-196.379-56.542-246.33-93.21-5.975-4.374-9.65-11.6-9.671-19.012v-57.384a35.347 35.347 0 016.857-20.922l15.583-21.085c8.336-11.312 20.757-14.31 33.98-14.31 4.988-56.953 16.794-97.604 45.024-127.354C155.194 5.77 226.56 0 256 0c29.441 0 100.807 5.77 154.557 62.722 28.19 29.75 40.036 70.401 45.025 127.354 13.263 0 25.602 2.936 33.958 14.31l15.583 21.127c4.476 6.077 6.878 13.345 6.878 20.88zm-97.666-26.075c-.677-13.058-11.292-18.19-22.338-21.824-11.64 7.309-25.848 10.183-39.46 10.183-14.454 0-41.432-3.47-63.872-25.869-5.667-5.625-9.527-14.454-12.155-24.247a212.902 212.902 0 00-20.469-1.088c-6.098 0-13.099.349-20.551 1.088-2.628 9.793-6.509 18.622-12.155 24.247-22.4 22.4-49.418 25.87-63.872 25.87-13.612 0-27.86-2.855-39.501-10.184-11.005 3.613-21.558 8.828-22.277 21.824-1.17 24.555-1.272 49.11-1.375 73.645-.041 12.318-.082 24.658-.288 36.976.062 7.166 4.374 13.818 10.882 16.774 52.97 24.124 103.045 36.278 149.137 36.278 46.01 0 96.085-12.154 149.014-36.278 6.508-2.956 10.84-9.608 10.881-16.774.637-36.832.124-73.809-1.642-110.62h.041zM107.521 168.97c8.643 8.623 24.966 14.392 42.56 14.392 13.448 0 39.03-2.874 60.156-24.329 9.28-8.951 15.05-31.35 14.413-54.079-.657-18.231-5.769-33.28-13.448-39.665-8.315-7.371-27.203-10.574-48.33-8.644-22.399 2.238-41.267 9.588-50.875 19.833-20.798 22.728-16.323 80.317-4.476 92.492zm130.556-56.008c.637 3.51.965 7.35 1.273 11.517 0 2.875 0 5.77-.308 8.952 6.406-.636 11.847-.636 16.959-.636s10.553 0 16.959.636c-.329-3.182-.329-6.077-.329-8.952.329-4.167.657-8.007 1.294-11.517-6.735-.637-12.812-.965-17.924-.965s-11.21.328-17.924.965zm49.275-8.008c-.637 22.728 5.133 45.128 14.413 54.08 21.105 21.454 46.708 24.328 60.155 24.328 17.596 0 33.918-5.769 42.561-14.392 11.847-12.175 16.322-69.764-4.476-92.492-9.608-10.245-28.476-17.595-50.875-19.833-21.127-1.93-40.015 1.273-48.33 8.644-7.679 6.385-12.791 21.434-13.448 39.665z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function PiIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 800 800"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
<path d="M517.36 400 H634.72 V634.72 H517.36 Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function AppStoreIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -2,12 +2,12 @@ import "~/styles.css";
|
||||
|
||||
export function SiteHeader() {
|
||||
return (
|
||||
<header className="flex items-center justify-between gap-4">
|
||||
<header className="flex flex-col items-center gap-4 md:flex-row md:justify-between">
|
||||
<a href="/" className="flex items-center gap-3">
|
||||
<img src="/logo.svg" alt="Paseo" className="w-6 h-6" />
|
||||
<span className="text-lg font-medium">Paseo</span>
|
||||
</a>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
<a
|
||||
href="/blog"
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import * as React from "react";
|
||||
import websitePackage from "../package.json";
|
||||
|
||||
declare const __DESKTOP_VERSION__: string | undefined;
|
||||
export function releaseBase(version: string) {
|
||||
return `https://github.com/getpaseo/paseo/releases/download/v${version}`;
|
||||
}
|
||||
|
||||
export const desktopVersion =
|
||||
typeof __DESKTOP_VERSION__ !== "undefined" ? __DESKTOP_VERSION__ : websitePackage.version;
|
||||
export const releaseBase = `https://github.com/getpaseo/paseo/releases/download/v${desktopVersion}`;
|
||||
export const macAppleSiliconDownloadUrl = `${releaseBase}/Paseo-${desktopVersion}-arm64.dmg`;
|
||||
export const macIntelDownloadUrl = `${releaseBase}/Paseo-${desktopVersion}-x64.dmg`;
|
||||
export const linuxAppImageDownloadUrl = `${releaseBase}/Paseo-${desktopVersion}-x86_64.AppImage`;
|
||||
export const linuxDebDownloadUrl = `${releaseBase}/Paseo-${desktopVersion}-amd64.deb`;
|
||||
export const linuxRpmDownloadUrl = `${releaseBase}/Paseo-${desktopVersion}-x86_64.rpm`;
|
||||
export function downloadUrls(version: string) {
|
||||
const base = releaseBase(version);
|
||||
return {
|
||||
macAppleSilicon: `${base}/Paseo-${version}-arm64.dmg`,
|
||||
macIntel: `${base}/Paseo-${version}-x64.dmg`,
|
||||
linuxAppImage: `${base}/Paseo-${version}-x86_64.AppImage`,
|
||||
linuxDeb: `${base}/Paseo-${version}-amd64.deb`,
|
||||
linuxRpm: `${base}/Paseo-${version}-x86_64.rpm`,
|
||||
windowsExe: `${base}/Paseo-Setup-${version}.exe`,
|
||||
androidApk: `${base}/paseo-v${version}-android.apk`,
|
||||
};
|
||||
}
|
||||
|
||||
export const appStoreUrl = "https://apps.apple.com/app/paseo-pocket-engineer/id6758887924";
|
||||
export const playStoreUrl = "https://play.google.com/store/apps/details?id=sh.paseo";
|
||||
@@ -25,32 +30,35 @@ export interface DownloadOption {
|
||||
icon: (props: React.SVGProps<SVGSVGElement>) => React.ReactElement;
|
||||
}
|
||||
|
||||
export const downloadOptions: DownloadOption[] = [
|
||||
{
|
||||
platform: "mac-silicon",
|
||||
label: "Mac",
|
||||
href: macAppleSiliconDownloadUrl,
|
||||
icon: AppleIcon,
|
||||
},
|
||||
{
|
||||
platform: "mac-intel",
|
||||
label: "Mac Intel",
|
||||
href: macIntelDownloadUrl,
|
||||
icon: AppleIcon,
|
||||
},
|
||||
{
|
||||
platform: "windows",
|
||||
label: "Windows",
|
||||
href: `${releaseBase}/Paseo-Setup-${desktopVersion}.exe`,
|
||||
icon: WindowsIcon,
|
||||
},
|
||||
{
|
||||
platform: "linux",
|
||||
label: "Linux",
|
||||
href: linuxAppImageDownloadUrl,
|
||||
icon: LinuxIcon,
|
||||
},
|
||||
];
|
||||
export function getDownloadOptions(version: string): DownloadOption[] {
|
||||
const urls = downloadUrls(version);
|
||||
return [
|
||||
{
|
||||
platform: "mac-silicon",
|
||||
label: "Mac",
|
||||
href: urls.macAppleSilicon,
|
||||
icon: AppleIcon,
|
||||
},
|
||||
{
|
||||
platform: "mac-intel",
|
||||
label: "Mac Intel",
|
||||
href: urls.macIntel,
|
||||
icon: AppleIcon,
|
||||
},
|
||||
{
|
||||
platform: "windows",
|
||||
label: "Windows",
|
||||
href: urls.windowsExe,
|
||||
icon: WindowsIcon,
|
||||
},
|
||||
{
|
||||
platform: "linux",
|
||||
label: "Linux",
|
||||
href: urls.linuxAppImage,
|
||||
icon: LinuxIcon,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function useDetectedPlatform(): Platform {
|
||||
const [platform, setPlatform] = React.useState<Platform>("mac-silicon");
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import websitePackage from "../package.json";
|
||||
|
||||
interface GitHubAsset {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface GitHubRelease {
|
||||
tag_name: string;
|
||||
assets: GitHubAsset[];
|
||||
prerelease: boolean;
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
const REQUIRED_ASSET_PATTERNS = [
|
||||
/Paseo-.*-arm64\.dmg$/, // Mac Apple Silicon
|
||||
/Paseo-.*-x86_64\.AppImage$/, // Linux AppImage
|
||||
/Paseo-Setup-.*\.exe$/, // Windows
|
||||
];
|
||||
|
||||
function hasRequiredAssets(release: GitHubRelease): boolean {
|
||||
return REQUIRED_ASSET_PATTERNS.every((pattern) =>
|
||||
release.assets.some((asset) => pattern.test(asset.name)),
|
||||
);
|
||||
}
|
||||
|
||||
function versionFromTag(tag: string): string {
|
||||
return tag.replace(/^v/, "");
|
||||
}
|
||||
|
||||
const GITHUB_RELEASES_URL =
|
||||
"https://api.github.com/repos/getpaseo/paseo/releases?per_page=10";
|
||||
|
||||
async function fetchLatestReadyRelease(): Promise<string> {
|
||||
const fallback = websitePackage.version.replace(/-.*$/, "");
|
||||
|
||||
try {
|
||||
const res = await fetch(GITHUB_RELEASES_URL, {
|
||||
headers: {
|
||||
Accept: "application/vnd.github+json",
|
||||
"User-Agent": "paseo-website",
|
||||
},
|
||||
// Cloudflare Workers: cache the upstream response with stale-while-revalidate.
|
||||
// Fresh for 60s, serve stale for up to 300s while revalidating in background.
|
||||
// In non-Workers environments this is ignored.
|
||||
cf: {
|
||||
cacheEverything: true,
|
||||
cacheTtl: 60,
|
||||
cacheKey: "github-releases-latest",
|
||||
},
|
||||
} as RequestInit);
|
||||
if (!res.ok) return fallback;
|
||||
|
||||
const releases = (await res.json()) as GitHubRelease[];
|
||||
const ready = releases.find(
|
||||
(r) => !r.prerelease && !r.draft && hasRequiredAssets(r),
|
||||
);
|
||||
return ready ? versionFromTag(ready.tag_name) : fallback;
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
export const getLatestRelease = createServerFn({ method: "GET" }).handler(
|
||||
async () => {
|
||||
const version = await fetchLatestReadyRelease();
|
||||
return { version };
|
||||
},
|
||||
);
|
||||
@@ -25,6 +25,7 @@ import { Route as DocsVoiceRouteImport } from './routes/docs/voice'
|
||||
import { Route as DocsUpdatesRouteImport } from './routes/docs/updates'
|
||||
import { Route as DocsSecurityRouteImport } from './routes/docs/security'
|
||||
import { Route as DocsConfigurationRouteImport } from './routes/docs/configuration'
|
||||
import { Route as DocsSkillsRouteImport } from './routes/docs/skills'
|
||||
import { Route as DocsCliRouteImport } from './routes/docs/cli'
|
||||
import { Route as DocsBestPracticesRouteImport } from './routes/docs/best-practices'
|
||||
import { Route as BlogSplatRouteImport } from './routes/blog/$'
|
||||
@@ -109,6 +110,11 @@ const DocsConfigurationRoute = DocsConfigurationRouteImport.update({
|
||||
path: '/configuration',
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
const DocsSkillsRoute = DocsSkillsRouteImport.update({
|
||||
id: '/skills',
|
||||
path: '/skills',
|
||||
getParentRoute: () => DocsRoute,
|
||||
} as any)
|
||||
const DocsCliRoute = DocsCliRouteImport.update({
|
||||
id: '/cli',
|
||||
path: '/cli',
|
||||
@@ -140,6 +146,7 @@ export interface FileRoutesByFullPath {
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/skills': typeof DocsSkillsRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
@@ -159,6 +166,7 @@ export interface FileRoutesByTo {
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/skills': typeof DocsSkillsRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
@@ -181,6 +189,7 @@ export interface FileRoutesById {
|
||||
'/docs/cli': typeof DocsCliRoute
|
||||
'/docs/configuration': typeof DocsConfigurationRoute
|
||||
'/docs/security': typeof DocsSecurityRoute
|
||||
'/docs/skills': typeof DocsSkillsRoute
|
||||
'/docs/updates': typeof DocsUpdatesRoute
|
||||
'/docs/voice': typeof DocsVoiceRoute
|
||||
'/docs/worktrees': typeof DocsWorktreesRoute
|
||||
@@ -204,6 +213,7 @@ export interface FileRouteTypes {
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/skills'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
@@ -223,6 +233,7 @@ export interface FileRouteTypes {
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/skills'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
@@ -244,6 +255,7 @@ export interface FileRouteTypes {
|
||||
| '/docs/cli'
|
||||
| '/docs/configuration'
|
||||
| '/docs/security'
|
||||
| '/docs/skills'
|
||||
| '/docs/updates'
|
||||
| '/docs/voice'
|
||||
| '/docs/worktrees'
|
||||
@@ -377,6 +389,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof DocsConfigurationRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/skills': {
|
||||
id: '/docs/skills'
|
||||
path: '/skills'
|
||||
fullPath: '/docs/skills'
|
||||
preLoaderRoute: typeof DocsSkillsRouteImport
|
||||
parentRoute: typeof DocsRoute
|
||||
}
|
||||
'/docs/cli': {
|
||||
id: '/docs/cli'
|
||||
path: '/cli'
|
||||
@@ -418,6 +437,7 @@ interface DocsRouteChildren {
|
||||
DocsCliRoute: typeof DocsCliRoute
|
||||
DocsConfigurationRoute: typeof DocsConfigurationRoute
|
||||
DocsSecurityRoute: typeof DocsSecurityRoute
|
||||
DocsSkillsRoute: typeof DocsSkillsRoute
|
||||
DocsUpdatesRoute: typeof DocsUpdatesRoute
|
||||
DocsVoiceRoute: typeof DocsVoiceRoute
|
||||
DocsWorktreesRoute: typeof DocsWorktreesRoute
|
||||
@@ -429,6 +449,7 @@ const DocsRouteChildren: DocsRouteChildren = {
|
||||
DocsCliRoute: DocsCliRoute,
|
||||
DocsConfigurationRoute: DocsConfigurationRoute,
|
||||
DocsSecurityRoute: DocsSecurityRoute,
|
||||
DocsSkillsRoute: DocsSkillsRoute,
|
||||
DocsUpdatesRoute: DocsUpdatesRoute,
|
||||
DocsVoiceRoute: DocsVoiceRoute,
|
||||
DocsWorktreesRoute: DocsWorktreesRoute,
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { createContext, useContext } from "react";
|
||||
import { Outlet, createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
|
||||
import { getLatestRelease } from "~/release";
|
||||
|
||||
interface ReleaseContext {
|
||||
version: string;
|
||||
}
|
||||
|
||||
const ReleaseCtx = createContext<ReleaseContext>({ version: "" });
|
||||
|
||||
export function useRelease(): ReleaseContext {
|
||||
return useContext(ReleaseCtx);
|
||||
}
|
||||
|
||||
export const Route = createRootRoute({
|
||||
loader: async () => {
|
||||
return getLatestRelease();
|
||||
},
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ charSet: "utf-8" },
|
||||
@@ -23,10 +38,13 @@ export const Route = createRootRoute({
|
||||
});
|
||||
|
||||
function RootComponent() {
|
||||
const release = Route.useLoaderData();
|
||||
return (
|
||||
<RootDocument>
|
||||
<Outlet />
|
||||
</RootDocument>
|
||||
<ReleaseCtx value={release}>
|
||||
<RootDocument>
|
||||
<Outlet />
|
||||
</RootDocument>
|
||||
</ReleaseCtx>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const navigation = [
|
||||
{ name: "Voice", href: "/docs/voice" },
|
||||
{ name: "Git worktrees", href: "/docs/worktrees" },
|
||||
{ name: "CLI", href: "/docs/cli" },
|
||||
{ name: "Skills", href: "/docs/skills" },
|
||||
{ name: "Configuration", href: "/docs/configuration" },
|
||||
{ name: "Security", href: "/docs/security" },
|
||||
{ name: "Best practices", href: "/docs/best-practices" },
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { pageMeta } from "~/meta";
|
||||
|
||||
export const Route = createFileRoute("/docs/skills")({
|
||||
head: () => ({
|
||||
meta: pageMeta(
|
||||
"Orchestration Skills - Paseo Docs",
|
||||
"Paseo orchestration skills: teach coding agents to spawn, coordinate, and manage other agents using slash commands.",
|
||||
),
|
||||
}),
|
||||
component: Skills,
|
||||
});
|
||||
|
||||
function Code({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="bg-card border border-border rounded-lg p-4 font-mono text-sm overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Skills() {
|
||||
return (
|
||||
<div className="space-y-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Orchestration Skills</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo ships orchestration skills that teach coding agents (Claude Code, Codex) how to use
|
||||
the Paseo CLI to spawn, coordinate, and manage other agents. Skills are slash commands your
|
||||
agent can invoke — they provide the prompts, context, and workflows so agents know how to
|
||||
orchestrate without you writing boilerplate. Install them from the desktop app's
|
||||
Integrations settings or via the CLI.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Installation */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Installation</h2>
|
||||
<p className="text-white/60 leading-relaxed">Two ways to install:</p>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
<strong>Desktop app:</strong> Settings → Integrations → Install
|
||||
</li>
|
||||
<li>
|
||||
<strong>Manual:</strong> <code className="font-mono">npx skills add getpaseo/paseo</code>{" "}
|
||||
— this installs to <code className="font-mono">~/.agents/skills/</code> and sets up
|
||||
symlinks for each agent.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* /paseo */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo</code> — CLI Reference
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The foundational skill. Loaded automatically by other skills. Contains the full Paseo CLI
|
||||
command reference so agents know how to run commands.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Not typically invoked directly by users — it's a reference that other skills depend on.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* /paseo-handoff */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo-handoff</code> — Task Handoff
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Hands off your current task to another agent with full context. The receiving agent gets a
|
||||
comprehensive prompt with: task description, relevant files, what's been tried, decisions
|
||||
made, and acceptance criteria.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Default provider is Codex. Can specify Claude (sonnet/opus). Supports{" "}
|
||||
<code className="font-mono">--worktree</code> for isolated git branches.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`/paseo-handoff hand off the auth fix to codex in a worktree
|
||||
/paseo-handoff hand this to claude opus for review`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* /paseo-loop */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo-loop</code> — Iterative Loops
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Runs an agent in a loop with automatic verification until an exit condition is met. Worker
|
||||
runs, verifier checks, repeat until done or max iterations. Supports different providers for
|
||||
worker vs verifier (e.g., Codex implements, Claude verifies).
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Stop conditions: <code className="font-mono">--max-iterations</code>,{" "}
|
||||
<code className="font-mono">--max-time</code>, or verification passes.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`/paseo-loop fix the failing tests, verify with npm test, max 5 iterations
|
||||
/paseo-loop use codex to implement, claude sonnet to verify, loop until tests pass`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* /paseo-orchestrator */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo-orchestrator</code> — Team Orchestration
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Builds and manages a team of agents coordinating through a shared chat room. You describe
|
||||
the work, it sets up roles, launches agents, and coordinates through chat. Uses a heartbeat
|
||||
schedule to check progress.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Cross-provider: typically Codex for implementation, Claude for review.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`/paseo-orchestrator spin up a team to implement the database migration, codex implements, claude reviews`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* /paseo-chat */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo-chat</code> — Chat Rooms
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use persistent chat rooms for asynchronous agent coordination. Create rooms, post messages,
|
||||
read history, wait for replies. Supports @mentions for specific agents or @everyone.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Typically used by the orchestrator skill, but can be used directly.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`/paseo-chat create a room called "backend-refactor" for coordinating the API changes
|
||||
/paseo-chat post to backend-refactor: "API endpoints are done, ready for review"`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* /paseo-committee */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">
|
||||
<code className="font-mono">/paseo-committee</code> — Committee Planning
|
||||
</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Forms a committee of two high-reasoning agents (Claude Opus + GPT 5.4) to analyze a problem
|
||||
before implementing. Both agents reason in parallel, then plans are merged. Useful when
|
||||
stuck, looping, or facing a hard architectural decision.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Agents are prevented from editing code — they only produce a plan.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`/paseo-committee why are the websocket connections dropping under load?
|
||||
/paseo-committee plan the auth system migration`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,13 +2,7 @@ import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
import { CommandDialog } from "~/components/command-dialog";
|
||||
import { pageMeta } from "~/meta";
|
||||
import {
|
||||
desktopVersion,
|
||||
linuxAppImageDownloadUrl,
|
||||
linuxDebDownloadUrl,
|
||||
linuxRpmDownloadUrl,
|
||||
releaseBase,
|
||||
macAppleSiliconDownloadUrl,
|
||||
macIntelDownloadUrl,
|
||||
downloadUrls,
|
||||
appStoreUrl,
|
||||
playStoreUrl,
|
||||
webAppUrl,
|
||||
@@ -19,6 +13,7 @@ import {
|
||||
TerminalIcon,
|
||||
GlobeIcon,
|
||||
} from "~/downloads";
|
||||
import { useRelease } from "~/routes/__root";
|
||||
import "~/styles.css";
|
||||
|
||||
export const Route = createFileRoute("/download")({
|
||||
@@ -32,6 +27,9 @@ export const Route = createFileRoute("/download")({
|
||||
});
|
||||
|
||||
function Download() {
|
||||
const { version } = useRelease();
|
||||
const urls = downloadUrls(version);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<div className="max-w-3xl mx-auto p-6 md:p-12">
|
||||
@@ -93,7 +91,7 @@ function Download() {
|
||||
|
||||
<h1 className="text-3xl md:text-4xl font-semibold tracking-tight mb-2">Download</h1>
|
||||
<p className="text-muted-foreground mb-10">
|
||||
v{desktopVersion}
|
||||
v{version}
|
||||
</p>
|
||||
|
||||
{/* Desktop */}
|
||||
@@ -111,8 +109,8 @@ function Download() {
|
||||
<span className="font-medium">macOS</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<DownloadPill href={macAppleSiliconDownloadUrl} label="Apple Silicon" />
|
||||
<DownloadPill href={macIntelDownloadUrl} label="Intel" />
|
||||
<DownloadPill href={urls.macAppleSilicon} label="Apple Silicon" />
|
||||
<DownloadPill href={urls.macIntel} label="Intel" />
|
||||
<CommandDialog
|
||||
trigger={
|
||||
<span className="inline-flex items-center justify-center rounded-full bg-foreground px-4 py-1.5 text-sm font-medium text-background hover:bg-foreground/85 transition-colors">
|
||||
@@ -133,7 +131,7 @@ function Download() {
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<DownloadPill
|
||||
href={`${releaseBase}/Paseo-Setup-${desktopVersion}.exe`}
|
||||
href={urls.windowsExe}
|
||||
label="Download"
|
||||
/>
|
||||
</div>
|
||||
@@ -147,11 +145,11 @@ function Download() {
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<DownloadPill
|
||||
href={linuxAppImageDownloadUrl}
|
||||
href={urls.linuxAppImage}
|
||||
label="AppImage"
|
||||
/>
|
||||
<DownloadPill href={linuxDebDownloadUrl} label="DEB" />
|
||||
<DownloadPill href={linuxRpmDownloadUrl} label="RPM" />
|
||||
<DownloadPill href={urls.linuxDeb} label="DEB" />
|
||||
<DownloadPill href={urls.linuxRpm} label="RPM" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -178,7 +176,7 @@ function Download() {
|
||||
external
|
||||
/>
|
||||
<DownloadPill
|
||||
href={`${releaseBase}/paseo-v${desktopVersion}-android.apk`}
|
||||
href={urls.androidApk}
|
||||
label="APK"
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user