diff --git a/CHANGELOG.md b/CHANGELOG.md index 03608fb77..d0071d0b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,7 +207,7 @@ ### Added -- Provider profiles — define custom providers in your Paseo config that appear alongside built-ins. Override a built-in's binary, env, or models, or create entirely new providers. See the [configuration guide](https://github.com/getpaseo/paseo/blob/main/docs/CUSTOM-PROVIDERS.md). +- Provider profiles — define custom providers in your Paseo config that appear alongside built-ins. Override a built-in's binary, env, or models, or create entirely new providers. See the [configuration guide](https://github.com/getpaseo/paseo/blob/main/docs/custom-providers.md). - ACP agent support — add any ACP-compatible agent to Paseo with `extends: "acp"` in your provider config. No code changes needed. - Choose provider and model when creating scheduled agents. - Max reasoning effort option for Opus 4.6 models. diff --git a/CLAUDE.md b/CLAUDE.md index cbdfafda5..b2210779d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,14 +19,14 @@ This is an npm workspace monorepo: | Doc | What's in it | | ---------------------------------------------------- | --------------------------------------------------------------------------------- | -| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System design, package layering, WebSocket protocol, agent lifecycle, data flow | -| [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md) | Type hygiene, error handling, state design, React patterns, file organization | -| [docs/TESTING.md](docs/TESTING.md) | TDD workflow, determinism, real dependencies over mocks, test organization | -| [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | Dev server, build sync gotchas, CLI reference, agent state, Playwright MCP | -| [docs/RELEASE.md](docs/RELEASE.md) | Release playbook, draft releases, completion checklist | -| [docs/CUSTOM-PROVIDERS.md](docs/CUSTOM-PROVIDERS.md) | Custom provider config: Z.AI, Alibaba/Qwen, ACP agents, profiles, custom binaries | -| [docs/ANDROID.md](docs/ANDROID.md) | App variants, local/cloud builds, EAS workflows | -| [docs/DESIGN.md](docs/DESIGN.md) | How to design features before implementation | +| [docs/architecture.md](docs/architecture.md) | System design, package layering, WebSocket protocol, agent lifecycle, data flow | +| [docs/coding-standards.md](docs/coding-standards.md) | Type hygiene, error handling, state design, React patterns, file organization | +| [docs/testing.md](docs/testing.md) | TDD workflow, determinism, real dependencies over mocks, test organization | +| [docs/development.md](docs/development.md) | Dev server, build sync gotchas, CLI reference, agent state, Playwright MCP | +| [docs/release.md](docs/release.md) | Release playbook, draft releases, completion checklist | +| [docs/custom-providers.md](docs/custom-providers.md) | Custom provider config: Z.AI, Alibaba/Qwen, ACP agents, profiles, custom binaries | +| [docs/android.md](docs/android.md) | App variants, local/cloud builds, EAS workflows | +| [docs/design.md](docs/design.md) | How to design features before implementation | | [SECURITY.md](SECURITY.md) | Relay threat model, E2E encryption, DNS rebinding, agent auth | ## Quick start @@ -41,7 +41,7 @@ npm run format # Auto-format with Biome npm run format:check # Check formatting without writing ``` -See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for full setup, build sync requirements, and debugging. +See [docs/development.md](docs/development.md) for full setup, build sync requirements, and debugging. ## Critical rules diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0dc3cc4d..29bcd6618 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,10 +27,10 @@ If you want to propose a direction change, start a conversation. Please read these first: - [README.md](README.md) -- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) -- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) -- [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md) -- [docs/TESTING.md](docs/TESTING.md) +- [docs/architecture.md](docs/architecture.md) +- [docs/development.md](docs/development.md) +- [docs/coding-standards.md](docs/coding-standards.md) +- [docs/testing.md](docs/testing.md) - [CLAUDE.md](CLAUDE.md) ## What is most helpful @@ -105,7 +105,7 @@ npm run dev:website npm run cli -- ls -a -g ``` -Read [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for build-sync gotchas, local state, ports, and daemon details. +Read [docs/development.md](docs/development.md) for build-sync gotchas, local state, ports, and daemon details. ## Multi-platform testing @@ -132,7 +132,7 @@ If you touch protocol or shared client/server behavior, read the compatibility n Paseo has explicit standards. Follow them. -The full guide lives in [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md). +The full guide lives in [docs/coding-standards.md](docs/coding-standards.md). ## PR checklist diff --git a/docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md b/docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md deleted file mode 100644 index 4a3f07554..000000000 --- a/docs/ATTACHMENT_BASED_REVIEW_CONTEXT_PLAN.md +++ /dev/null @@ -1,158 +0,0 @@ -# Attachment-Based Review Context Plan - -## Goal - -Use structured attachments as the source of truth for review context during agent creation. - -This covers two related behaviors: - -1. Provider-facing context for the agent prompt -2. Worktree checkout behavior when creating a worktree from a review item - -The design should stay compatible with future forge support such as GitLab. - -## Core Direction - -- Do not parse the prompt to discover issue/PR intent. -- Do not introduce deeply nested transport objects for this flow. -- Make the attachment itself the discriminated union. -- Keep forge-specific checkout logic below the attachment layer. -- Let each agent provider translate attachments into its own prompt/input format. - -## Initial Attachment Types - -Two initial structured attachment types: - -- `github_pr` -- `github_issue` - -With separate MIME types: - -- `application/github-pr` -- `application/github-issue` - -Proposed wire shape: - -```ts -type AgentAttachment = - | { - type: "github_pr"; - mimeType: "application/github-pr"; - number: number; - title: string; - url: string; - body?: string | null; - baseRefName?: string | null; - headRefName?: string | null; - } - | { - type: "github_issue"; - mimeType: "application/github-issue"; - number: number; - title: string; - url: string; - body?: string | null; - }; -``` - -## Backward Compatibility - -- Add new optional `attachments` fields; keep existing `images` fields. -- Unknown attachment discriminators must be dropped during schema normalization instead of failing the full request. -- Malformed attachment entries should also be ignored safely. -- Old clients continue working because `attachments` is optional. -- New clients can send `attachments` without breaking older flows that still rely on `images`. - -## Request-Level Changes - -Add optional `attachments` to: - -- `create_agent_request` -- `send_agent_message_request` - -Existing `initialPrompt` remains plain user text. - -The selected GitHub PR/issue becomes a structured attachment instead of being injected into prompt text as the primary source of truth. - -## App Responsibilities - -When the user selects a GitHub item: - -- If it is a PR, create a `github_pr` attachment -- If it is an issue, create a `github_issue` attachment -- Include the attachment in the create-agent request - -The UI can still render friendly labels and previews from the same data. - -The app should stop treating PR/issue context as prompt-only metadata. - -## Worktree Creation Behavior - -During agent creation, if worktree creation is requested: - -- Inspect normalized attachments -- If a `github_pr` attachment is present, use it to drive checkout for the worktree - -The important rule: - -- attachment type identifies the review object -- server-side git logic decides how to check it out - -This keeps the attachment contract simple while allowing fork-safe implementation details. - -## Checkout Resolution - -The attachment itself should not encode the full checkout strategy. - -Instead, the server should resolve checkout from the `github_pr` attachment using forge-aware logic. - -Examples of possible implementation strategies: - -- `gh pr checkout` -- `git fetch origin refs/pull//head:` -- another GitHub-aware resolver if needed later - -This is intentionally a server implementation detail, not part of the attachment schema. - -## Provider Responsibilities - -Each provider adapter should receive normalized attachments and decide how to represent them for the model. - -Examples: - -- Claude: render attachment into text blocks -- Codex: inject as blocks or text -- OpenCode: send as file/resource-like input where appropriate -- ACP: convert to resource/text forms as supported - -The session layer should not hardcode one translation strategy for all providers. - -## Suggested Implementation Order - -1. Add tolerant attachment schema and normalization in shared/server message handling. -2. Thread `attachments` through app -> daemon client -> session layer. -3. Update create-agent UI flows to emit `github_pr` / `github_issue` attachments from GitHub selection. -4. Teach worktree creation to inspect attachments and special-case `github_pr`. -5. Update provider adapters to translate attachments into provider-specific prompt/input forms. -6. Optionally migrate image transport into the same attachment mechanism later. - -## Files Likely Involved - -- `packages/server/src/shared/messages.ts` -- `packages/server/src/client/daemon-client.ts` -- `packages/server/src/server/session.ts` -- `packages/server/src/server/worktree-session.ts` -- `packages/server/src/server/agent/agent-sdk-types.ts` -- `packages/server/src/server/agent/providers/claude-agent.ts` -- `packages/server/src/server/agent/providers/codex-app-server-agent.ts` -- `packages/server/src/server/agent/providers/opencode-agent.ts` -- `packages/server/src/server/agent/providers/acp-agent.ts` -- `packages/app/src/screens/new-workspace-screen.tsx` -- `packages/app/src/screens/agent/draft-agent-screen.tsx` -- `packages/app/src/contexts/session-context.tsx` - -## Notes - -- The current image path can remain in place initially. -- The plan intentionally keeps checkout concerns separate from provider prompt translation. -- Future forge support should add new attachment discriminators rather than expanding GitHub-only nested fields. diff --git a/docs/AD-HOC-DAEMON-TESTING.md b/docs/ad-hoc-daemon-testing.md similarity index 100% rename from docs/AD-HOC-DAEMON-TESTING.md rename to docs/ad-hoc-daemon-testing.md diff --git a/docs/ANDROID.md b/docs/android.md similarity index 100% rename from docs/ANDROID.md rename to docs/android.md diff --git a/docs/ARCHITECTURE.md b/docs/architecture.md similarity index 100% rename from docs/ARCHITECTURE.md rename to docs/architecture.md diff --git a/docs/CODING_STANDARDS.md b/docs/coding-standards.md similarity index 100% rename from docs/CODING_STANDARDS.md rename to docs/coding-standards.md diff --git a/docs/CUSTOM-PROVIDERS.md b/docs/custom-providers.md similarity index 100% rename from docs/CUSTOM-PROVIDERS.md rename to docs/custom-providers.md diff --git a/docs/DATA_MODEL.md b/docs/data-model.md similarity index 100% rename from docs/DATA_MODEL.md rename to docs/data-model.md diff --git a/docs/DESIGN-SYSTEM.md b/docs/design-system.md similarity index 99% rename from docs/DESIGN-SYSTEM.md rename to docs/design-system.md index 11fe829ec..bee8c23ba 100644 --- a/docs/DESIGN-SYSTEM.md +++ b/docs/design-system.md @@ -1,4 +1,4 @@ -# DESIGN-SYSTEM.md +# Design system Tokens — every color, font size, weight, spacing step, radius, icon size — live in `packages/app/src/styles/theme.ts`. diff --git a/docs/DESIGN.md b/docs/design.md similarity index 93% rename from docs/DESIGN.md rename to docs/design.md index b44f5b7be..5ef2ee16b 100644 --- a/docs/DESIGN.md +++ b/docs/design.md @@ -17,7 +17,7 @@ Before designing anything new, understand what exists: - Where does similar functionality live? - What patterns does the codebase already use? -- What layers exist? (See [ARCHITECTURE.md](./ARCHITECTURE.md)) +- What layers exist? (See [architecture.md](./architecture.md)) - What types and data shapes are already defined? New features rarely mean only new code. Usually they require modifying existing interfaces, extending existing types, or refactoring to accommodate the new functionality. Identify what needs to change, not just what needs to be added. @@ -38,7 +38,7 @@ If you can't define verification, you don't understand the feature well enough y - What types are needed? - Use discriminated unions — make impossible states impossible -- One canonical type per concept (see [CODING_STANDARDS.md](./CODING_STANDARDS.md)) +- One canonical type per concept (see [coding-standards.md](./coding-standards.md)) ### Layers diff --git a/docs/DEVELOPMENT.md b/docs/development.md similarity index 100% rename from docs/DEVELOPMENT.md rename to docs/development.md diff --git a/docs/FILE_ICONS.md b/docs/file-icons.md similarity index 100% rename from docs/FILE_ICONS.md rename to docs/file-icons.md diff --git a/docs/GLOSSARY.md b/docs/glossary.md similarity index 100% rename from docs/GLOSSARY.md rename to docs/glossary.md diff --git a/docs/MOBILE_TESTING.md b/docs/mobile-testing.md similarity index 99% rename from docs/MOBILE_TESTING.md rename to docs/mobile-testing.md index 2e36be7dd..280626234 100644 --- a/docs/MOBILE_TESTING.md +++ b/docs/mobile-testing.md @@ -261,4 +261,4 @@ xcrun simctl ui booted appearance dark # set dark xcrun simctl ui booted appearance light # set light ``` -Expo dev server logs are in the tmux pane running `npm run dev`. Daemon logs are at `$PASEO_HOME/daemon.log` (see [DEVELOPMENT.md](DEVELOPMENT.md)). +Expo dev server logs are in the tmux pane running `npm run dev`. Daemon logs are at `$PASEO_HOME/daemon.log` (see [development.md](development.md)). diff --git a/docs/plan-approval-normalization.md b/docs/plan-approval-normalization.md deleted file mode 100644 index 182d15ec5..000000000 --- a/docs/plan-approval-normalization.md +++ /dev/null @@ -1,68 +0,0 @@ -# Plan Approval Normalization - -## Goal - -Normalize plan approval across providers so the UI renders one consistent plan approval card and action row, while each provider keeps its own execution quirks behind the session permission interface. - -## Compatibility Constraints - -- Older clients must remain compatible with newer daemons. -- All new wire fields must be optional. -- Existing plan permissions without action metadata must still render and work. -- Existing question permissions must keep their current behavior. - -## Design - -### Shared abstraction - -Add optional permission action definitions to the shared permission request/response types. - -- Permission requests may include `actions`. -- Permission responses may include `selectedActionId`. -- `kind: "plan"` remains the normalized concept for plan approval. -- The UI renders actions from the permission request instead of hardcoding provider-specific buttons. - -### Claude - -Keep Claude's plan permission flow, but enrich it with explicit action definitions. - -- Always expose `Reject`. -- Always expose `Implement`. -- If the agent entered plan mode from a more permissive mode like `bypassPermissions`, also expose `Implement with `. -- Resolve the selected action entirely inside `respondToPermission()`. - -### Codex - -Synthesize a normalized `kind: "plan"` permission after a Codex plan-mode turn completes with a plan result. - -- Emit a plan permission with `Reject` and `Implement` actions. -- On `Implement`, disable `plan_mode`, disable `fast_mode`, and automatically start a follow-up implementation turn. -- On `Reject`, resolve without starting a follow-up turn. -- Keep the implementation prompt and state transitions inside the Codex provider. - -### Manager and state sync - -After permission resolution, refresh provider-derived state so the UI sees internal mode/feature changes without knowing provider quirks. - -- Refresh current mode -- Refresh pending permissions -- Refresh runtime info -- Refresh features -- Persist refreshed state - -### UI - -Render plan permissions through the existing plan card, but generate buttons from normalized permission actions. - -- If `actions` are absent, fall back to legacy buttons. -- Plan cards should use `Implement` as the default primary label. -- Do not add provider-specific rendering branches. - -## Verification - -1. Shared schema/type tests for optional `actions` and `selectedActionId` -2. App tests for generic plan-action rendering -3. Claude tests for third action when resuming from a more permissive mode -4. Codex tests for synthetic plan approval and automatic implementation follow-up -5. Manager tests for post-permission state refresh -6. `npm run typecheck` diff --git a/docs/PRODUCT.md b/docs/product.md similarity index 100% rename from docs/PRODUCT.md rename to docs/product.md diff --git a/docs/PROVIDERS.md b/docs/providers.md similarity index 100% rename from docs/PROVIDERS.md rename to docs/providers.md diff --git a/docs/RELEASE.md b/docs/release.md similarity index 100% rename from docs/RELEASE.md rename to docs/release.md diff --git a/docs/TESTING.md b/docs/testing.md similarity index 100% rename from docs/TESTING.md rename to docs/testing.md diff --git a/docs/UNISTYLES.md b/docs/unistyles.md similarity index 100% rename from docs/UNISTYLES.md rename to docs/unistyles.md diff --git a/packages/server/CLAUDE.md b/packages/server/CLAUDE.md index 3d829b93f..76e9222ec 100644 --- a/packages/server/CLAUDE.md +++ b/packages/server/CLAUDE.md @@ -192,7 +192,7 @@ npm run db:query -- "SELECT * FROM agent_timeline_rows..." | File | What it covers | | ---------------------------------------------------------- | ------------------------------------------------ | | [../CLAUDE.md](../CLAUDE.md) | Repository overview, critical rules, quick start | -| [../docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md) | System design, WebSocket protocol, data flow | -| [../docs/CODING_STANDARDS.md](../docs/CODING_STANDARDS.md) | Type hygiene, error handling, React patterns | -| [../docs/TESTING.md](../docs/TESTING.md) | TDD workflow, determinism, real deps over mocks | +| [../docs/architecture.md](../docs/architecture.md) | System design, WebSocket protocol, data flow | +| [../docs/coding-standards.md](../docs/coding-standards.md) | Type hygiene, error handling, React patterns | +| [../docs/testing.md](../docs/testing.md) | TDD workflow, determinism, real deps over mocks | | [../SECURITY.md](../SECURITY.md) | Relay threat model, E2E encryption | diff --git a/packages/website/src/docs.ts b/packages/website/src/docs.ts new file mode 100644 index 000000000..153ef71a4 --- /dev/null +++ b/packages/website/src/docs.ts @@ -0,0 +1,81 @@ +interface DocFrontmatter { + title: string; + description: string; + nav: string; + order: number; +} + +export interface Doc { + slug: string; + href: string; + frontmatter: DocFrontmatter; + content: string; +} + +function parseFrontmatter(raw: string): { data: Record; content: string } { + const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/); + if (!match) return { data: {}, content: raw }; + + const data: Record = {}; + for (const line of match[1].split("\n")) { + const colonIdx = line.indexOf(":"); + if (colonIdx === -1) continue; + const key = line.slice(0, colonIdx).trim(); + const value = line + .slice(colonIdx + 1) + .trim() + .replace(/^["']|["']$/g, ""); + data[key] = value; + } + + return { data, content: match[2] }; +} + +const docModules = import.meta.glob("../../../public-docs/**/*.md", { + eager: true, + query: "?raw", + import: "default", +}) as Record; + +function pathToSlug(path: string): string { + const after = path.split("/public-docs/")[1] ?? path; + const noExt = after.replace(/\.md$/, ""); + return noExt === "index" ? "" : noExt; +} + +function loadDocs(): Doc[] { + const docs: Doc[] = []; + + for (const [path, raw] of Object.entries(docModules)) { + const { data, content } = parseFrontmatter(raw); + const slug = pathToSlug(path); + const href = slug === "" ? "/docs" : `/docs/${slug}`; + const order = Number.parseInt(data.order ?? "999", 10); + + docs.push({ + slug, + href, + frontmatter: { + title: data.title ?? "", + description: data.description ?? "", + nav: data.nav ?? data.title ?? slug, + order: Number.isFinite(order) ? order : 999, + }, + content, + }); + } + + docs.sort((a, b) => a.frontmatter.order - b.frontmatter.order); + return docs; +} + +let cached: Doc[] | undefined; + +export function getDocs(): Doc[] { + if (!cached) cached = loadDocs(); + return cached; +} + +export function getDoc(slug: string): Doc | undefined { + return getDocs().find((d) => d.slug === slug); +} diff --git a/packages/website/src/routeTree.gen.ts b/packages/website/src/routeTree.gen.ts index bb184b656..a844c9ef3 100644 --- a/packages/website/src/routeTree.gen.ts +++ b/packages/website/src/routeTree.gen.ts @@ -20,15 +20,7 @@ import { Route as BlogRouteImport } from "./routes/blog"; import { Route as IndexRouteImport } from "./routes/index"; import { Route as DocsIndexRouteImport } from "./routes/docs/index"; import { Route as BlogIndexRouteImport } from "./routes/blog/index"; -import { Route as DocsWorktreesRouteImport } from "./routes/docs/worktrees"; -import { Route as DocsVoiceRouteImport } from "./routes/docs/voice"; -import { Route as DocsUpdatesRouteImport } from "./routes/docs/updates"; -import { Route as DocsSkillsRouteImport } from "./routes/docs/skills"; -import { Route as DocsSecurityRouteImport } from "./routes/docs/security"; -import { Route as DocsProvidersRouteImport } from "./routes/docs/providers"; -import { Route as DocsConfigurationRouteImport } from "./routes/docs/configuration"; -import { Route as DocsCliRouteImport } from "./routes/docs/cli"; -import { Route as DocsBestPracticesRouteImport } from "./routes/docs/best-practices"; +import { Route as DocsSplatRouteImport } from "./routes/docs/$"; import { Route as BlogSplatRouteImport } from "./routes/blog/$"; const PrivacyRoute = PrivacyRouteImport.update({ @@ -86,49 +78,9 @@ const BlogIndexRoute = BlogIndexRouteImport.update({ path: "/", getParentRoute: () => BlogRoute, } as any); -const DocsWorktreesRoute = DocsWorktreesRouteImport.update({ - id: "/worktrees", - path: "/worktrees", - getParentRoute: () => DocsRoute, -} as any); -const DocsVoiceRoute = DocsVoiceRouteImport.update({ - id: "/voice", - path: "/voice", - getParentRoute: () => DocsRoute, -} as any); -const DocsUpdatesRoute = DocsUpdatesRouteImport.update({ - id: "/updates", - path: "/updates", - getParentRoute: () => DocsRoute, -} as any); -const DocsSkillsRoute = DocsSkillsRouteImport.update({ - id: "/skills", - path: "/skills", - getParentRoute: () => DocsRoute, -} as any); -const DocsSecurityRoute = DocsSecurityRouteImport.update({ - id: "/security", - path: "/security", - getParentRoute: () => DocsRoute, -} as any); -const DocsProvidersRoute = DocsProvidersRouteImport.update({ - id: "/providers", - path: "/providers", - getParentRoute: () => DocsRoute, -} as any); -const DocsConfigurationRoute = DocsConfigurationRouteImport.update({ - id: "/configuration", - path: "/configuration", - getParentRoute: () => DocsRoute, -} as any); -const DocsCliRoute = DocsCliRouteImport.update({ - id: "/cli", - path: "/cli", - getParentRoute: () => DocsRoute, -} as any); -const DocsBestPracticesRoute = DocsBestPracticesRouteImport.update({ - id: "/best-practices", - path: "/best-practices", +const DocsSplatRoute = DocsSplatRouteImport.update({ + id: "/$", + path: "/$", getParentRoute: () => DocsRoute, } as any); const BlogSplatRoute = BlogSplatRouteImport.update({ @@ -148,15 +100,7 @@ export interface FileRoutesByFullPath { "/opencode": typeof OpencodeRoute; "/privacy": typeof PrivacyRoute; "/blog/$": typeof BlogSplatRoute; - "/docs/best-practices": typeof DocsBestPracticesRoute; - "/docs/cli": typeof DocsCliRoute; - "/docs/configuration": typeof DocsConfigurationRoute; - "/docs/providers": typeof DocsProvidersRoute; - "/docs/security": typeof DocsSecurityRoute; - "/docs/skills": typeof DocsSkillsRoute; - "/docs/updates": typeof DocsUpdatesRoute; - "/docs/voice": typeof DocsVoiceRoute; - "/docs/worktrees": typeof DocsWorktreesRoute; + "/docs/$": typeof DocsSplatRoute; "/blog/": typeof BlogIndexRoute; "/docs/": typeof DocsIndexRoute; } @@ -169,15 +113,7 @@ export interface FileRoutesByTo { "/opencode": typeof OpencodeRoute; "/privacy": typeof PrivacyRoute; "/blog/$": typeof BlogSplatRoute; - "/docs/best-practices": typeof DocsBestPracticesRoute; - "/docs/cli": typeof DocsCliRoute; - "/docs/configuration": typeof DocsConfigurationRoute; - "/docs/providers": typeof DocsProvidersRoute; - "/docs/security": typeof DocsSecurityRoute; - "/docs/skills": typeof DocsSkillsRoute; - "/docs/updates": typeof DocsUpdatesRoute; - "/docs/voice": typeof DocsVoiceRoute; - "/docs/worktrees": typeof DocsWorktreesRoute; + "/docs/$": typeof DocsSplatRoute; "/blog": typeof BlogIndexRoute; "/docs": typeof DocsIndexRoute; } @@ -193,15 +129,7 @@ export interface FileRoutesById { "/opencode": typeof OpencodeRoute; "/privacy": typeof PrivacyRoute; "/blog/$": typeof BlogSplatRoute; - "/docs/best-practices": typeof DocsBestPracticesRoute; - "/docs/cli": typeof DocsCliRoute; - "/docs/configuration": typeof DocsConfigurationRoute; - "/docs/providers": typeof DocsProvidersRoute; - "/docs/security": typeof DocsSecurityRoute; - "/docs/skills": typeof DocsSkillsRoute; - "/docs/updates": typeof DocsUpdatesRoute; - "/docs/voice": typeof DocsVoiceRoute; - "/docs/worktrees": typeof DocsWorktreesRoute; + "/docs/$": typeof DocsSplatRoute; "/blog/": typeof BlogIndexRoute; "/docs/": typeof DocsIndexRoute; } @@ -218,15 +146,7 @@ export interface FileRouteTypes { | "/opencode" | "/privacy" | "/blog/$" - | "/docs/best-practices" - | "/docs/cli" - | "/docs/configuration" - | "/docs/providers" - | "/docs/security" - | "/docs/skills" - | "/docs/updates" - | "/docs/voice" - | "/docs/worktrees" + | "/docs/$" | "/blog/" | "/docs/"; fileRoutesByTo: FileRoutesByTo; @@ -239,15 +159,7 @@ export interface FileRouteTypes { | "/opencode" | "/privacy" | "/blog/$" - | "/docs/best-practices" - | "/docs/cli" - | "/docs/configuration" - | "/docs/providers" - | "/docs/security" - | "/docs/skills" - | "/docs/updates" - | "/docs/voice" - | "/docs/worktrees" + | "/docs/$" | "/blog" | "/docs"; id: @@ -262,15 +174,7 @@ export interface FileRouteTypes { | "/opencode" | "/privacy" | "/blog/$" - | "/docs/best-practices" - | "/docs/cli" - | "/docs/configuration" - | "/docs/providers" - | "/docs/security" - | "/docs/skills" - | "/docs/updates" - | "/docs/voice" - | "/docs/worktrees" + | "/docs/$" | "/blog/" | "/docs/"; fileRoutesById: FileRoutesById; @@ -366,67 +270,11 @@ declare module "@tanstack/react-router" { preLoaderRoute: typeof BlogIndexRouteImport; parentRoute: typeof BlogRoute; }; - "/docs/worktrees": { - id: "/docs/worktrees"; - path: "/worktrees"; - fullPath: "/docs/worktrees"; - preLoaderRoute: typeof DocsWorktreesRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/voice": { - id: "/docs/voice"; - path: "/voice"; - fullPath: "/docs/voice"; - preLoaderRoute: typeof DocsVoiceRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/updates": { - id: "/docs/updates"; - path: "/updates"; - fullPath: "/docs/updates"; - preLoaderRoute: typeof DocsUpdatesRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/skills": { - id: "/docs/skills"; - path: "/skills"; - fullPath: "/docs/skills"; - preLoaderRoute: typeof DocsSkillsRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/security": { - id: "/docs/security"; - path: "/security"; - fullPath: "/docs/security"; - preLoaderRoute: typeof DocsSecurityRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/providers": { - id: "/docs/providers"; - path: "/providers"; - fullPath: "/docs/providers"; - preLoaderRoute: typeof DocsProvidersRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/configuration": { - id: "/docs/configuration"; - path: "/configuration"; - fullPath: "/docs/configuration"; - preLoaderRoute: typeof DocsConfigurationRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/cli": { - id: "/docs/cli"; - path: "/cli"; - fullPath: "/docs/cli"; - preLoaderRoute: typeof DocsCliRouteImport; - parentRoute: typeof DocsRoute; - }; - "/docs/best-practices": { - id: "/docs/best-practices"; - path: "/best-practices"; - fullPath: "/docs/best-practices"; - preLoaderRoute: typeof DocsBestPracticesRouteImport; + "/docs/$": { + id: "/docs/$"; + path: "/$"; + fullPath: "/docs/$"; + preLoaderRoute: typeof DocsSplatRouteImport; parentRoute: typeof DocsRoute; }; "/blog/$": { @@ -452,28 +300,12 @@ const BlogRouteChildren: BlogRouteChildren = { const BlogRouteWithChildren = BlogRoute._addFileChildren(BlogRouteChildren); interface DocsRouteChildren { - DocsBestPracticesRoute: typeof DocsBestPracticesRoute; - DocsCliRoute: typeof DocsCliRoute; - DocsConfigurationRoute: typeof DocsConfigurationRoute; - DocsProvidersRoute: typeof DocsProvidersRoute; - DocsSecurityRoute: typeof DocsSecurityRoute; - DocsSkillsRoute: typeof DocsSkillsRoute; - DocsUpdatesRoute: typeof DocsUpdatesRoute; - DocsVoiceRoute: typeof DocsVoiceRoute; - DocsWorktreesRoute: typeof DocsWorktreesRoute; + DocsSplatRoute: typeof DocsSplatRoute; DocsIndexRoute: typeof DocsIndexRoute; } const DocsRouteChildren: DocsRouteChildren = { - DocsBestPracticesRoute: DocsBestPracticesRoute, - DocsCliRoute: DocsCliRoute, - DocsConfigurationRoute: DocsConfigurationRoute, - DocsProvidersRoute: DocsProvidersRoute, - DocsSecurityRoute: DocsSecurityRoute, - DocsSkillsRoute: DocsSkillsRoute, - DocsUpdatesRoute: DocsUpdatesRoute, - DocsVoiceRoute: DocsVoiceRoute, - DocsWorktreesRoute: DocsWorktreesRoute, + DocsSplatRoute: DocsSplatRoute, DocsIndexRoute: DocsIndexRoute, }; diff --git a/packages/website/src/routes/docs.tsx b/packages/website/src/routes/docs.tsx index 7c71f569e..eee705b7c 100644 --- a/packages/website/src/routes/docs.tsx +++ b/packages/website/src/routes/docs.tsx @@ -1,28 +1,21 @@ import { createFileRoute, Link, Outlet } from "@tanstack/react-router"; +import { getDocs } from "~/docs"; import "~/styles.css"; export const Route = createFileRoute("/docs")({ component: DocsLayout, }); -const navigation = [ - { name: "Getting started", href: "/docs" }, - { name: "Updates", href: "/docs/updates" }, - { name: "Voice", href: "/docs/voice" }, - { name: "Git worktrees", href: "/docs/worktrees" }, - { name: "CLI", href: "/docs/cli" }, - { name: "Skills", href: "/docs/skills" }, - { name: "Providers", href: "/docs/providers" }, - { name: "Configuration", href: "/docs/configuration" }, - { name: "Security", href: "/docs/security" }, - { name: "Best practices", href: "/docs/best-practices" }, -]; - const ACTIVE_OPTIONS_EXACT = { exact: true }; const MOBILE_ACTIVE_PROPS = { className: "text-foreground" }; const DESKTOP_ACTIVE_PROPS = { className: "bg-muted text-foreground" }; function DocsLayout() { + const navigation = getDocs().map((doc) => ({ + name: doc.frontmatter.nav, + href: doc.href, + })); + return (
{/* Mobile header */} @@ -31,7 +24,7 @@ function DocsLayout() { Paseo Paseo -
diff --git a/packages/website/src/routes/docs/$.tsx b/packages/website/src/routes/docs/$.tsx new file mode 100644 index 000000000..a538ad311 --- /dev/null +++ b/packages/website/src/routes/docs/$.tsx @@ -0,0 +1,28 @@ +import { createFileRoute } from "@tanstack/react-router"; +import ReactMarkdown from "react-markdown"; +import { getDoc } from "~/docs"; +import { pageMeta } from "~/meta"; + +export const Route = createFileRoute("/docs/$")({ + head: ({ params }) => { + const slug = params._splat ?? ""; + const doc = getDoc(slug); + if (!doc) return { meta: pageMeta("Not Found - Paseo Docs", "Doc not found.") }; + return { + meta: pageMeta(`${doc.frontmatter.title} - Paseo Docs`, doc.frontmatter.description), + }; + }, + component: DocsPage, +}); + +function DocsPage() { + const { _splat } = Route.useParams(); + const slug = _splat ?? ""; + const doc = getDoc(slug); + + if (!doc) { + return

Doc not found.

; + } + + return {doc.content}; +} diff --git a/packages/website/src/routes/docs/best-practices.tsx b/packages/website/src/routes/docs/best-practices.tsx deleted file mode 100644 index 9e6a7e08c..000000000 --- a/packages/website/src/routes/docs/best-practices.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/best-practices")({ - head: () => ({ - meta: pageMeta( - "Best Practices - Paseo Docs", - "Tips for getting the most out of Paseo and mobile-first agent workflows.", - ), - }), - component: BestPractices, -}); - -function BestPractices() { - return ( -
-
-

Best Practices

-

- What I've learned from using Paseo daily. Not rules, just patterns that have worked - for me. -

-
- -
-

Agents replace typing, not thinking

-

- Your role has changed. You're no longer the one writing code line by line. - You're the one making decisions: what to build, how it should work, what the - architecture looks like. The agent executes, but you direct. -

-

- You can't just say "implement feature X" and walk away. You still have to - do the hard part: deciding what to build, how it fits into the system, what trade-offs to - make. Thinking is not optional. At least for now, agents replace the typing, not the - thinking. -

-
- -
-

Verification loops

-

- The agent needs a way to verify its work. TDD is one implementation of this pattern: get - the agent to write a failing test, verify it fails for the right reasons, then tell it to - make the test pass. The agent can loop on its own because it knows what "done" - means. -

-
- -
-

Invest in tooling

-

- It's not just test runners. For web apps, something like Playwright MCP lets the - agent take screenshots and verify UI changes. For a SaaS app I built a CLI that wraps all - the business logic so the agent could launch jobs, check statuses, and scrape data without - going through the UI. -

-

- Code is cheap with coding agents. I would have never written that CLI before because it - felt like wasted effort. Now I bootstrap tooling first. It pays off exponentially. -

-
- -
-

Agents are cheap

-

- Don't be shy about running multiple agents. Paseo lets you launch agents in isolated - worktrees. Kick one off with voice while walking, then kick off another. They work - independently. You get a notification when they're done. -

-
- -
-

Use voice extensively

-

- It's much more natural to use voice to communicate ideas and pull them out of your - brain. The agent will parse and organize your thoughts better than if you try to write the - perfect prompt. You don't need to organize anything. Just talk. -

-

- Current speech-to-text models are really good. They catch accents, acronyms, technical - terms. And even when they don't, the LLM will infer what you meant. -

-
- -
-

Understand the type of work

-

- Sometimes you need to plan: design a spec, verify it, get the agent to follow through. - Maybe it takes a couple of agents to work through it. Other times it's - conversational: kick off a single agent and start talking, asking questions. Match your - approach to the task. -

-
- -
-

Iterate and refactor often

-

- Don't expect perfect. Expect working. Make it work, make it correct, make it - beautiful. Each iteration gets you closer. With tests, refactoring is cheap. -

-

- I don't let myself add too many features before stopping to refactor. Sometimes I - kick off an agent and have it trace code paths, explain dependencies, show me how modules - connect. I make mental notes during code review and circle back. -

-
- -
-

Use agents to check agents

-

- If an agent implements something and you ask it to review its own work, it will never find - issues. Launch a separate agent with a fresh context to review the first agent's - code. It will catch things the first agent missed or glossed over. An agent might say - it's done when it's not. Another agent can detect that. -

-
- -
-

Learn your agents' quirks

-

- People argue about which model is better. That's the wrong question. Each model has - strengths and weaknesses. Knowing them is more useful than chasing benchmarks. Benchmarks - don't mean anything. You need to try the models yourself to form an opinion. -

-

- I use Claude Code as my main driver because it's quick and uses tools well. But - sometimes it jumps to conclusions and gives up too easily. Codex is frustratingly slow but - goes deep, doesn't stop, and is methodical. It's also stubborn and too serious. - These aren't good or bad traits, just differences you learn to work around. Use the - right model for the job. -

-
-
- ); -} diff --git a/packages/website/src/routes/docs/cli.tsx b/packages/website/src/routes/docs/cli.tsx deleted file mode 100644 index 0a41ae16e..000000000 --- a/packages/website/src/routes/docs/cli.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/cli")({ - head: () => ({ - meta: pageMeta( - "CLI - Paseo Docs", - "Paseo CLI reference: manage agents, daemons, permissions, and worktrees from your terminal.", - ), - }), - component: CLI, -}); - -function Code({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function CLI() { - return ( -
-
-

CLI

-

- The Paseo CLI lets you manage agents from your terminal. It's the same interface - exposed by the daemon's API, so anything you can do in the app you can do from the - command line. -

-
- - {/* Agent orchestration callout */} -
-
- Agent orchestration: You can tell coding agents to use the Paseo CLI to - spawn and manage other agents. This enables multi-agent workflows where one agent - delegates subtasks to others and waits for results. -
-
- - {/* Quick reference */} -
-

Quick reference

- -
{`paseo run "fix the tests"           # Start an agent
-paseo ls                             # List running agents
-paseo attach                     # Stream agent output
-paseo send  "also fix linting"  # Send follow-up task
-paseo logs                       # View agent timeline
-paseo stop                       # Stop an agent`}
-
-
- - {/* Running agents */} -
-

Running agents

-

- Use paseo run to start a new agent with a task: -

- -
{`paseo run "implement user authentication"
-paseo run --provider codex "refactor the API layer"
-paseo run --detach "run the full test suite"  # background
-paseo run --worktree feature-x "implement feature X"
-paseo run --output-schema schema.json "extract release notes"
-paseo run --output-schema '{"type":"object","properties":{"summary":{"type":"string"}},"required":["summary"]}' "summarize release notes"`}
-
-

- The --worktree flag creates the agent in an isolated - git worktree, useful for parallel feature development. -

-

- Use --output-schema to return only matching JSON - output. You can pass a schema file path or an inline JSON schema object. This mode cannot - be used with --detach. -

-

- By default, paseo run waits for completion. Use{" "} - --detach to run in the background. -

-
- - {/* Listing agents */} -
-

Listing agents

- -
{`paseo ls                    # Running agents in current directory
-paseo ls -a                 # Include completed/stopped agents
-paseo ls -g                 # All directories
-paseo ls -a -g --json       # Full list as JSON`}
-
-
- - {/* Streaming output */} -
-

Streaming output

-

- Use paseo attach to stream an agent's output in - real-time: -

- -
{`paseo attach abc123   # Attach to agent (Ctrl+C to detach)`}
-
-

- Agent IDs can be shortened — abc works if it's - unambiguous. -

-
- - {/* Sending messages */} -
-

Sending messages

-

- Send follow-up tasks to a running or idle agent: -

- -
{`paseo send  "now run the tests"
-paseo send  --image screenshot.png "what's wrong here?"
-paseo send  --no-wait "queue this task"`}
-
-
- - {/* Viewing logs */} -
-

Viewing logs

- -
{`paseo logs                   # Full timeline
-paseo logs  -f               # Follow (streaming)
-paseo logs  --tail 10        # Last 10 entries
-paseo logs  --filter tools   # Only tool calls`}
-
-
- - {/* Waiting for agents */} -
-

Waiting for agents

-

- Block until an agent finishes its current task: -

- -
{`paseo wait 
-paseo wait  --timeout 60   # 60 second timeout`}
-
-

- Useful in scripts or when one agent needs to wait for another. -

-
- - {/* Permissions */} -
-

Permissions

-

- Agents may request permission for certain actions. Manage these from the CLI: -

- -
{`paseo permit ls                # List pending requests
-paseo permit allow         # Allow all pending for agent
-paseo permit deny  --all   # Deny all pending`}
-
-
- - {/* Agent modes */} -
-

Agent modes

-

- Change an agent's operational mode (provider-specific): -

- -
{`paseo agent mode  --list   # Show available modes
-paseo agent mode  bypass   # Set bypass mode
-paseo agent mode  plan     # Set plan mode`}
-
-
- - {/* Daemon management */} -
-

Daemon management

- -
{`paseo daemon start             # Start the daemon
-paseo daemon status            # Check status
-paseo daemon stop              # Stop the daemon`}
-
-

- Use PASEO_HOME to run multiple isolated daemon - instances. -

-
- - {/* Multi-agent workflows */} -
-

Multi-agent workflows

-

- The CLI is designed to be used by agents themselves. You can instruct an agent to spawn - sub-agents for parallel work: -

- -
{`# Agent A spawns Agent B and waits for it
-paseo run --detach "implement the API" --name api-agent
-paseo wait api-agent
-paseo logs api-agent --tail 5`}
-
-

Simple implement + verify loop:

- -
{`# Requires jq
-while true; do
-  paseo run --provider codex "make the tests pass" >/dev/null
-
-  verdict=$(paseo run --provider claude --output-schema '{"type":"object","properties":{"criteria_met":{"type":"boolean"}},"required":["criteria_met"],"additionalProperties":false}' "ensure tests all pass")
-  if echo "$verdict" | jq -e '.criteria_met == true' >/dev/null; then
-    echo "criteria met"
-    break
-  fi
-done`}
-
-

- This pattern enables hierarchical task decomposition — a lead agent can break down work, - delegate to specialists, and synthesize results. -

-
- - {/* Output formats */} -
-

Output formats

-

- Most commands support multiple output formats for scripting: -

- -
{`paseo ls --json                # JSON output
-paseo ls --format yaml         # YAML output
-paseo ls -q                    # IDs only (quiet)`}
-
-
- - {/* Global options */} -
-

Global options

-
    -
  • - --host <host:port> — connect to a different - daemon -
  • -
  • - --json — JSON output -
  • -
  • - -q, --quiet — minimal output -
  • -
  • - --no-color — disable colors -
  • -
-
-
- ); -} diff --git a/packages/website/src/routes/docs/configuration.tsx b/packages/website/src/routes/docs/configuration.tsx deleted file mode 100644 index 69fcd132b..000000000 --- a/packages/website/src/routes/docs/configuration.tsx +++ /dev/null @@ -1,261 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/configuration")({ - head: () => ({ - meta: pageMeta( - "Configuration - Paseo Docs", - "Configure Paseo via config.json, environment variables, and CLI overrides.", - ), - }), - component: Configuration, -}); - -const CUSTOM_PROVIDERS_URL = "https://github.com/getpaseo/paseo/blob/main/docs/CUSTOM-PROVIDERS.md"; - -function Configuration() { - return ( -
-
-

Configuration

-

- Paseo loads configuration from a single JSON file in your Paseo home directory, with - optional environment variable and CLI overrides. -

-
- -
-

Where config lives

-

- By default, Paseo uses ~/.paseo as its home directory. - The configuration file is: -

-
- $ - ~/.paseo/config.json -
-

- You can change the home directory by setting PASEO_HOME{" "} - or passing --home to{" "} - paseo daemon start. -

-
- -
-

Precedence

-

Paseo merges configuration in this order:

-
    -
  1. Defaults
  2. -
  3. - config.json -
  4. -
  5. Environment variables
  6. -
  7. CLI flags
  8. -
-

- Lists append across sources (for example, hostnames and - cors.allowedOrigins). -

-
- -
-

Example

-

- Minimal example that configures listening address, hostnames, and MCP: -

-
-          {`{
-  "$schema": "https://paseo.sh/schemas/paseo.config.v1.json",
-  "version": 1,
-  "daemon": {
-    "listen": "127.0.0.1:6767",
-    "hostnames": ["localhost", ".localhost"],
-    "mcp": { "enabled": true }
-  }
-}`}
-        
-

- daemon.hostnames is the primary field. The old{" "} - daemon.allowedHosts name still works as a deprecated - alias for backward compatibility. -

-
- -
-

Agent providers

-

- Agent providers — both the first-class ones Paseo ships with and custom entries you add - under agents.providers — are documented on their own - page. -

-

- See{" "} - - Providers - {" "} - for first-class providers, how to point Claude at Anthropic-compatible endpoints (Z.AI, - Alibaba/Qwen), multiple profiles, custom binaries, ACP agents, and the{" "} - additionalModels merge behavior. The full field - reference lives on GitHub at{" "} - - docs/CUSTOM-PROVIDERS.md - - . -

-
- -
-

Voice

-

- Voice is configured through features.dictation and{" "} - features.voiceMode, with provider credentials under{" "} - providers. -

-

- For voice philosophy, architecture, and complete local/OpenAI setup examples, see{" "} - - Voice docs - - . -

-
- -
-

Logging

-

- Daemon logging uses separate console and file sinks by default: -

-
    -
  • - Console: info and above -
  • -
  • - File ($PASEO_HOME/daemon.log):{" "} - trace and above -
  • -
  • - File rotation: 10m max file size,{" "} - 2 retained files total (active + 1 rotated) -
  • -
-
-          {`{
-  "log": {
-    "console": {
-      "level": "info",
-      "format": "pretty"
-    },
-    "file": {
-      "level": "trace",
-      "path": "daemon.log",
-      "rotate": {
-        "maxSize": "10m",
-        "maxFiles": 2
-      }
-    }
-  }
-}`}
-        
-

- Legacy fields log.level and{" "} - log.format are still supported and map to the new - destination settings. -

-
- -
-

Common env vars

-
    -
  • - PASEO_HOME — set Paseo home directory -
  • -
  • - PASEO_LISTEN — override{" "} - daemon.listen -
  • -
  • - PASEO_HOSTNAMES — override/extend{" "} - daemon.hostnames -
  • -
  • - PASEO_ALLOWED_HOSTS — deprecated alias for{" "} - PASEO_HOSTNAMES -
  • -
  • - PASEO_LOG_CONSOLE_LEVEL — override{" "} - log.console.level -
  • -
  • - PASEO_LOG_FILE_LEVEL — override{" "} - log.file.level -
  • -
  • - PASEO_LOG_FILE_PATH — override{" "} - log.file.path -
  • -
  • - PASEO_LOG_FILE_ROTATE_SIZE — override{" "} - log.file.rotate.maxSize -
  • -
  • - PASEO_LOG_FILE_ROTATE_COUNT — override{" "} - log.file.rotate.maxFiles -
  • -
  • - PASEO_LOG,{" "} - PASEO_LOG_FORMAT — legacy log overrides (still - supported) -
  • -
  • - OPENAI_API_KEY — override OpenAI provider key -
  • -
  • - PASEO_VOICE_LLM_PROVIDER — override voice LLM - provider (claude,{" "} - codex, opencode) -
  • -
  • - PASEO_DICTATION_STT_PROVIDER,{" "} - PASEO_VOICE_STT_PROVIDER,{" "} - PASEO_VOICE_TTS_PROVIDER — override voice provider - selection (local or{" "} - openai) -
  • -
  • - PASEO_LOCAL_MODELS_DIR — control local model - directory -
  • -
  • - PASEO_DICTATION_LOCAL_STT_MODEL — override local - dictation STT model -
  • -
  • - PASEO_VOICE_LOCAL_STT_MODEL,{" "} - PASEO_VOICE_LOCAL_TTS_MODEL — override local voice - STT/TTS models -
  • -
  • - PASEO_VOICE_LOCAL_TTS_SPEAKER_ID,{" "} - PASEO_VOICE_LOCAL_TTS_SPEED — optional local voice - TTS tuning -
  • -
-
- -
-

Schema

-

- For editor autocomplete/validation, set $schema to: -

-
- https://paseo.sh/schemas/paseo.config.v1.json -
-
-
- ); -} diff --git a/packages/website/src/routes/docs/index.tsx b/packages/website/src/routes/docs/index.tsx index 5c1e9f903..752b43aa4 100644 --- a/packages/website/src/routes/docs/index.tsx +++ b/packages/website/src/routes/docs/index.tsx @@ -1,170 +1,21 @@ import { createFileRoute } from "@tanstack/react-router"; +import ReactMarkdown from "react-markdown"; +import { getDoc } from "~/docs"; import { pageMeta } from "~/meta"; export const Route = createFileRoute("/docs/")({ - head: () => ({ - meta: pageMeta( - "Getting Started - Paseo Docs", - "Learn how to set up and use Paseo to manage your coding agents from anywhere.", - ), - }), - component: GettingStarted, + head: () => { + const doc = getDoc(""); + if (!doc) return { meta: pageMeta("Docs - Paseo", "Paseo documentation.") }; + return { + meta: pageMeta(`${doc.frontmatter.title} - Paseo Docs`, doc.frontmatter.description), + }; + }, + component: DocsIndex, }); -function GettingStarted() { - return ( -
-
-

Getting Started

-

- Paseo has three main pieces: the daemon is the local server that manages your agents, the - app is the client you use from mobile, web, or desktop, and the CLI is the terminal - interface that can also launch the daemon. -

-
- -
-

Prerequisites

-

- Paseo manages existing agent CLIs. Install at least one agent and make sure it already - works with your credentials before you set up Paseo. -

- -
- -
-

Desktop App

-

- Download the desktop app from{" "} - - paseo.sh/download - {" "} - or the{" "} - - GitHub releases page - - . Open it and you're done. -

-

- The desktop app bundles and manages its own daemon automatically, so you do not need a - separate CLI install on that machine unless you want it. -

-

- On first launch, you may briefly see a startup screen while the local server starts and - the app connects to it. After that, connect from your phone by scanning the QR code in - Settings if you want mobile access. -

-
- -
-

CLI / Server

-

- Use this path for headless setups, servers, or remote machines where you want the daemon - running without the desktop app. -

-
- $ - npm install -g @getpaseo/cli -
-
- $ - paseo -
-

- Paseo prints a QR code in the terminal. Scan it from the mobile app, or enter the daemon - address manually from another client. -

-

- Configuration and local state live under PASEO_HOME. -

-
- -
-

Voice Setup

-

- Paseo includes first-class voice support with a local-first architecture and configurable - speech providers. -

-

- For architecture, local model behavior, and provider configuration, see the Voice docs - page. -

- - Voice docs - -
- -
-

Next

- -
-
- ); +function DocsIndex() { + const doc = getDoc(""); + if (!doc) return

Doc not found.

; + return {doc.content}; } diff --git a/packages/website/src/routes/docs/providers.tsx b/packages/website/src/routes/docs/providers.tsx deleted file mode 100644 index b03aaed88..000000000 --- a/packages/website/src/routes/docs/providers.tsx +++ /dev/null @@ -1,346 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/providers")({ - head: () => ({ - meta: pageMeta( - "Providers - Paseo Docs", - "First-class agent providers in Paseo, and how to configure custom providers, ACP agents, and profiles.", - ), - }), - component: Providers, -}); - -const CUSTOM_PROVIDERS_URL = "https://github.com/getpaseo/paseo/blob/main/docs/CUSTOM-PROVIDERS.md"; - -function Providers() { - return ( -
-
-

Providers

-

- A provider is an agent CLI that Paseo knows how to launch, stream, and control. Paseo - ships with first-class providers for the major coding agents, and lets you add your own - through config.json — either by pointing an existing - provider at a different API, adding extra profiles, or plugging in any{" "} - - ACP - - -compatible agent. -

-
- -
-

First-class providers

-

- These work out of the box once the underlying CLI is installed and authenticated. Paseo - discovers them automatically, wires up modes, and exposes them in the app and CLI. -

-
    -
  • - claude — Anthropic's Claude Code. Multi-tool - assistant with MCP support, streaming, and deep reasoning. -
  • -
  • - codex — OpenAI's Codex workspace agent with - sandbox controls and optional network access. -
  • -
  • - opencode — Open-source coding assistant with - multi-provider model support. -
  • -
  • - copilot — GitHub Copilot via ACP, with dynamic modes - and session support. -
  • -
  • - pi — Minimal terminal-based coding agent with - multi-provider LLM support. -
  • -
-
- -
-

Custom providers

-

- Everything beyond the defaults lives under{" "} - agents.providers in{" "} - ~/.paseo/config.json. You can: -

-
    -
  • - Extend a first-class provider to point at a different API (Z.AI, - Alibaba/Qwen, a proxy, a self-hosted endpoint). -
  • -
  • - Add profiles — multiple entries against the same underlying provider - with different credentials or curated model lists. -
  • -
  • - Override the binary — run a nightly build, a wrapper script, or a - Docker image instead of the installed CLI. -
  • -
  • - Add ACP agents — Gemini CLI, Hermes, or any agent speaking the Agent - Client Protocol over stdio. -
  • -
  • - Disable a provider you don't use. -
  • -
-

- Provider IDs must be lowercase alphanumeric with hyphens ( - /^[a-z][a-z0-9-]*$/). Every custom entry needs{" "} - extends (a first-class provider ID or{" "} - "acp") and a{" "} - label. -

-

- The examples below are a quick tour. The full, up-to-date reference is on GitHub:{" "} - - docs/CUSTOM-PROVIDERS.md - - . -

-
- -
-

Extending a first-class provider

-
-          {`{
-  "agents": {
-    "providers": {
-      "my-claude": {
-        "extends": "claude",
-        "label": "My Claude",
-        "env": {
-          "ANTHROPIC_API_KEY": "sk-ant-...",
-          "ANTHROPIC_BASE_URL": "https://my-proxy.example.com/v1"
-        }
-      }
-    }
-  }
-}`}
-        
-
- -
-

Z.AI (GLM) coding plan

-

- Z.AI exposes GLM models through an Anthropic-compatible endpoint. Point{" "} - ANTHROPIC_BASE_URL at their API and use{" "} - ANTHROPIC_AUTH_TOKEN for the key. Third-party endpoints - don't support Anthropic's server-side tools, so disable{" "} - WebSearch. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "zai": {
-        "extends": "claude",
-        "label": "ZAI",
-        "env": {
-          "ANTHROPIC_AUTH_TOKEN": "",
-          "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
-          "API_TIMEOUT_MS": "3000000"
-        },
-        "disallowedTools": ["WebSearch"],
-        "models": [
-          { "id": "glm-5-turbo", "label": "GLM 5 Turbo", "isDefault": true },
-          { "id": "glm-5.1", "label": "GLM 5.1" }
-        ]
-      }
-    }
-  }
-}`}
-        
-
- -
-

Alibaba Cloud (Qwen) coding plan

-

- Alibaba's coding plan routes Claude Code to Qwen models via an Anthropic-compatible - API. Subscription keys look like sk-sp-... and must be - created in the Singapore region. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "qwen": {
-        "extends": "claude",
-        "label": "Qwen (Alibaba)",
-        "env": {
-          "ANTHROPIC_AUTH_TOKEN": "sk-sp-",
-          "ANTHROPIC_BASE_URL": "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic"
-        },
-        "disallowedTools": ["WebSearch"],
-        "models": [
-          { "id": "qwen3.5-plus", "label": "Qwen 3.5 Plus", "isDefault": true },
-          { "id": "qwen3-coder-next", "label": "Qwen 3 Coder Next" }
-        ]
-      }
-    }
-  }
-}`}
-        
-
- -
-

Multiple profiles

-

- Create as many entries as you want against the same first-class provider. Each one shows - up as a separate option in the app with its own credentials and models. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "claude-work": {
-        "extends": "claude",
-        "label": "Claude (Work)",
-        "env": { "ANTHROPIC_API_KEY": "sk-ant-work-..." }
-      },
-      "claude-personal": {
-        "extends": "claude",
-        "label": "Claude (Personal)",
-        "env": { "ANTHROPIC_API_KEY": "sk-ant-personal-..." }
-      }
-    }
-  }
-}`}
-        
-
- -
-

Custom binary

-

- command is an array — first element is the binary, the - rest are arguments. It fully replaces the default launch command for that provider. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "claude": {
-        "command": ["/opt/claude-nightly/claude"]
-      }
-    }
-  }
-}`}
-        
-
- -
-

ACP providers

-

- Any agent that speaks{" "} - - ACP - {" "} - over stdio can be added with extends: "acp"{" "} - and a command. Paseo spawns the process, sends an{" "} - initialize JSON-RPC request, and the agent reports its - capabilities, modes, and models at runtime. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "gemini": {
-        "extends": "acp",
-        "label": "Google Gemini",
-        "command": ["gemini", "--acp"]
-      },
-      "hermes": {
-        "extends": "acp",
-        "label": "Hermes",
-        "command": ["hermes", "acp"]
-      }
-    }
-  }
-}`}
-        
-
- -
-

Adding or relabeling models

-

- models replaces the model list entirely.{" "} - additionalModels merges with runtime-discovered models - (ACP) or with models — use it to add an extra entry or - relabel a discovered one without redeclaring the full list. An entry with the same{" "} - id as a discovered model updates it in place. -

-
-          {`{
-  "agents": {
-    "providers": {
-      "gemini": {
-        "extends": "acp",
-        "label": "Google Gemini",
-        "command": ["gemini", "--acp"],
-        "additionalModels": [
-          { "id": "experimental-model", "label": "Experimental", "isDefault": true },
-          { "id": "gemini-2.5-pro", "label": "Gemini 2.5 Pro (preferred)" }
-        ]
-      }
-    }
-  }
-}`}
-        
-
- -
-

Disabling a provider

-
-          {`{
-  "agents": {
-    "providers": {
-      "copilot": { "enabled": false }
-    }
-  }
-}`}
-        
-
- -
-

Full reference

-

- For the complete field reference (extends,{" "} - label, command,{" "} - env, models,{" "} - additionalModels,{" "} - disallowedTools,{" "} - enabled, order), - model and thinking-option schemas, and deeper examples for each plan, see{" "} - - docs/CUSTOM-PROVIDERS.md - {" "} - on GitHub. -

-
-
- ); -} diff --git a/packages/website/src/routes/docs/security.tsx b/packages/website/src/routes/docs/security.tsx deleted file mode 100644 index a4e298226..000000000 --- a/packages/website/src/routes/docs/security.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/security")({ - head: () => ({ - meta: pageMeta( - "Security - Paseo Docs", - "Security model for Paseo: architecture overview, connection methods, relay encryption, and best practices.", - ), - }), - component: Security, -}); - -function Callout({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function Security() { - return ( -
-
-

Security

-

- Paseo follows a client-server architecture, similar to Docker. The daemon runs on your - machine and manages your coding agents. Clients (the mobile app, CLI, or web interface) - connect to the daemon to monitor and control those agents. -

-

- Your code never leaves your machine. Paseo is a local-first tool that connects directly to - your development environment. -

-
- - {/* Architecture Overview */} -
-

Architecture

-

- The Paseo daemon can run anywhere you want to execute agents: your laptop, a Mac Mini, a - VPS, or a Docker container. The daemon listens for connections and manages agent - lifecycles. -

-

- Clients connect to the daemon over WebSocket. There are two ways to establish this - connection: -

-
    -
  • - Relay connection (recommended) — The daemon - connects outbound to our relay server, and clients meet it there. No open ports - required. -
  • -
  • - Direct connection — The daemon listens on a - network address and clients connect directly -
  • -
-
- - {/* Relay Connection */} -
-

Relay connections (recommended)

-

- The relay is the simplest way to connect from your phone. It requires no VPN setup, no - port forwarding, and no firewall configuration. The daemon can stay bound to localhost or - a socket file — it connects outbound to the relay, and your phone meets it there. -

- - - The relay is designed to be untrusted. All traffic between your phone and - daemon is end-to-end encrypted. The relay server cannot read your messages, see your code, - or modify traffic without detection. Even if the relay is compromised, your data remains - protected. - - -

How it works

-
    -
  1. - The daemon generates a persistent ECDH keypair and stores it in{" "} - $PASEO_HOME/daemon-keypair.json -
  2. -
  3. - When you scan the QR code or click the pairing link, your phone receives the - daemon's public key -
  4. -
  5. - Your phone sends a handshake message with its own public key. The daemon will not accept - any commands until this handshake completes. -
  6. -
  7. - Both sides perform an ECDH key exchange to derive a shared secret. All subsequent - messages are encrypted with AES-256-GCM. -
  8. -
-

- The relay sees only: IP addresses, timing, message sizes, and session IDs. It cannot read - message contents, forge messages, or derive encryption keys from observing the handshake. -

- -

Why the relay can't attack you

-

- The daemon requires a valid cryptographic handshake before processing any commands. A - compromised relay cannot: -

-
    -
  • - Send commands — Without your phone's - private key, it cannot complete the handshake -
  • -
  • - Read your traffic — All messages are - encrypted with AES-256-GCM after the handshake -
  • -
  • - Forge messages — GCM provides authenticated - encryption; tampered messages are rejected -
  • -
  • - Replay old messages — Each session derives - fresh encryption keys -
  • -
- -

Trust model

-

- The QR code or pairing link is the trust anchor. It contains the daemon's public key, - which is required to establish the encrypted connection. Treat it like a password — - don't share it publicly. -

-

- If you believe a pairing offer has been compromised, restart the daemon to generate a new - session ID and rotate the relay pairing. -

-
- - {/* Direct Connection */} -
-

Direct connections

-

- By default, the daemon listens on 127.0.0.1:6767{" "} - (localhost only). This is safe for local CLI usage but not reachable from your phone or - other devices. -

- -

Socket file (CLI only)

-

- For maximum isolation, you can configure the daemon to listen on a Unix socket file - instead of a TCP port. This prevents any network access entirely — only processes on the - same machine can connect. The CLI supports this mode, but the mobile app and web interface - require a network connection. -

- -

VPN access

-

- If you prefer direct connections over the relay, you can use a VPN like{" "} - - Tailscale - - . Tailscale creates a private network between your devices, so you can access your daemon - without exposing it to the public internet. -

-

To set this up:

-
    -
  1. - Install Tailscale on your machine and phone and join them to the same{" "} - - tailnet - -
  2. -
  3. - Configure the daemon to listen on your Tailscale IP (e.g.,{" "} - 100.x.y.z:6767) -
  4. -
  5. - Add your Tailscale hostname to hostnames and{" "} - cors.allowedOrigins -
  6. -
  7. - Add the daemon as a direct connection in the Paseo app using the Tailscale address -
  8. -
- -

Binding to 0.0.0.0

-
- Warning: Binding to 0.0.0.0 makes the - daemon reachable on all network interfaces, including public Wi-Fi and local networks. - This can expose your daemon to unauthorized access. If you must bind to all interfaces, - ensure you have proper firewall rules and review your{" "} - hostnames configuration. -
-
- - {/* DNS Rebinding Protection */} -
-

DNS rebinding protection

-

- CORS is not a complete security boundary. It - controls which browser origins can make requests, but does not prevent a malicious website - from resolving its domain to your local machine (DNS rebinding). -

-

- Paseo uses a host allowlist to validate the Host header - on incoming requests. Requests with unrecognized hosts are rejected. -

-

- Configure via daemon.hostnames in{" "} - config.json: -

-
    -
  • - Default ([]): allow{" "} - localhost,{" "} - *.localhost, and all IP addresses -
  • -
  • - ['.example.com']: allow{" "} - example.com and any subdomain, plus defaults -
  • -
  • - true: allow any host (not recommended) -
  • -
-
- - {/* Agent Authentication */} -
-

Agent authentication

-

- Paseo wraps agent CLIs (Claude Code, Codex, OpenCode) but does not manage their - authentication. Each agent provider handles its own credentials: -

-
    -
  • - Claude Code — authenticates via - Anthropic's OAuth flow, stored in ~/.claude/ -
  • -
  • - Codex — uses your OpenAI API key or OAuth - session -
  • -
  • - OpenCode — configured via provider-specific - API keys -
  • -
-

- Paseo never stores or transmits provider API keys. Agents run in your user context with - your existing credentials. -

-
- - {/* Best Practices Summary */} -
-

Recommendations

-
    -
  • - Use the relay for mobile access — it's - the simplest option and all traffic is end-to-end encrypted -
  • -
  • - Treat the QR code like a password — anyone - with the pairing offer can connect to your daemon -
  • -
  • - Never bind to 0.0.0.0 unless you understand - the implications and have proper firewall rules -
  • -
  • - Keep your daemon updated — security - improvements are released regularly -
  • -
-
-
- ); -} diff --git a/packages/website/src/routes/docs/skills.tsx b/packages/website/src/routes/docs/skills.tsx deleted file mode 100644 index 34887a508..000000000 --- a/packages/website/src/routes/docs/skills.tsx +++ /dev/null @@ -1,165 +0,0 @@ -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 ( -
- {children} -
- ); -} - -function Skills() { - return ( -
-
-

Orchestration Skills

-

- 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. -

-
- - {/* Installation */} -
-

Installation

-

Two ways to install:

-
    -
  • - Desktop app: Settings → Integrations → Install -
  • -
  • - Manual:{" "} - npx skills add getpaseo/paseo — this installs to{" "} - ~/.agents/skills/ and sets up symlinks for each - agent. -
  • -
-
- - {/* /paseo */} -
-

- /paseo — CLI Reference -

-

- The foundational skill. Loaded automatically by other skills. Contains the full Paseo CLI - command reference so agents know how to run commands. -

-

- Not typically invoked directly by users — it's a reference that other skills depend - on. -

-
- - {/* /paseo-handoff */} -
-

- /paseo-handoff — Task Handoff -

-

- 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. -

-

- Default provider is Codex. Can specify Claude (sonnet/opus). Supports{" "} - --worktree for isolated git branches. -

- -
{`/paseo-handoff hand off the auth fix to codex in a worktree
-/paseo-handoff hand this to claude opus for review`}
-
-
- - {/* /paseo-loop */} -
-

- /paseo-loop — Iterative Loops -

-

- 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). -

-

- Stop conditions: --max-iterations,{" "} - --max-time, or verification passes. -

- -
{`/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`}
-
-
- - {/* /paseo-orchestrator */} -
-

- /paseo-orchestrator — Team Orchestration -

-

- 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. -

-

- Cross-provider: typically Codex for implementation, Claude for review. -

- -
{`/paseo-orchestrator spin up a team to implement the database migration, codex implements, claude reviews`}
-
-
- - {/* /paseo-chat */} -
-

- /paseo-chat — Chat Rooms -

-

- Use persistent chat rooms for asynchronous agent coordination. Create rooms, post - messages, read history, wait for replies. Supports @mentions for specific agents or - @everyone. -

-

- Typically used by the orchestrator skill, but can be used directly. -

- -
{`/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"`}
-
-
- - {/* /paseo-committee */} -
-

- /paseo-committee — Committee Planning -

-

- 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. -

-

- Agents are prevented from editing code — they only produce a plan. -

- -
{`/paseo-committee why are the websocket connections dropping under load?
-/paseo-committee plan the auth system migration`}
-
-
-
- ); -} diff --git a/packages/website/src/routes/docs/updates.tsx b/packages/website/src/routes/docs/updates.tsx deleted file mode 100644 index 1bfc6973e..000000000 --- a/packages/website/src/routes/docs/updates.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/updates")({ - head: () => ({ - meta: pageMeta( - "Updates - Paseo Docs", - "How to update Paseo daemon and apps across web, desktop, and mobile.", - ), - }), - component: UpdatesDocs, -}); - -function UpdatesDocs() { - return ( -
-
-

Updates

-

- Keep your daemon and apps current to get the latest fixes and features. -

-
- -
-

Version compatibility

-

- For now, daemon and app versions should be kept in lockstep. If your daemon is version X, - make sure your clients are also version X. -

-
- -
-

Update the daemon

-

Install the latest CLI/daemon package globally:

-
- $ - npm install -g @getpaseo/cli@latest -
-

Then restart the daemon:

-
- $ - paseo daemon restart -
-
- -
-

Web app

-

- - app.paseo.sh - {" "} - is always up to date. No manual update needed. -

-
- -
-

Desktop app

-

- Download the latest desktop build from the GitHub releases page and install it over your - current version. -

- - Paseo releases - -
- -
-

Mobile apps

-

- Mobile apps are available on the App Store and Play Store. Update through your respective - store. Store versions may lag behind the latest release due to review processes. -

-
-
- ); -} diff --git a/packages/website/src/routes/docs/voice.tsx b/packages/website/src/routes/docs/voice.tsx deleted file mode 100644 index 15822888c..000000000 --- a/packages/website/src/routes/docs/voice.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/voice")({ - head: () => ({ - meta: pageMeta( - "Voice - Paseo Docs", - "Paseo voice architecture, local-first model execution, and provider configuration.", - ), - }), - component: VoiceDocs, -}); - -function VoiceDocs() { - return ( -
-
-

Voice

-

- Paseo has first-class voice support for dictation and realtime conversations with your - coding environment. -

-
- -
-

Philosophy

-

- Voice is local-first. You can run speech fully on-device, or choose OpenAI for speech - features. For voice reasoning/orchestration, Paseo reuses agent providers already - installed and authenticated on your machine. -

-

- This keeps credentials and execution in your environment and avoids introducing a separate - cloud-only voice stack. -

-
- -
-

Architecture

-
    -
  • - Speech I/O: STT and TTS providers per feature (local{" "} - or openai) -
  • -
  • Local speech runtime: ONNX models executed on CPU by default
  • -
  • - Voice LLM orchestration: hidden agent session using your configured provider ( - claude, codex, or{" "} - opencode) -
  • -
  • Tooling path: MCP stdio bridge for voice tools and agent control
  • -
-
- -
-

Local Speech

-

- Local speech defaults to model IDs{" "} - parakeet-tdt-0.6b-v3-int8 (STT) and{" "} - kokoro-en-v0_19 (TTS, speaker 0 / voice 00). -

-

- Missing models are downloaded at daemon startup into{" "} - $PASEO_HOME/models/local-speech. Downloads happen only - for missing files. -

-
-          {`{
-  "version": 1,
-  "features": {
-    "dictation": { "stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v3-int8" } },
-    "voiceMode": {
-      "llm": { "provider": "claude", "model": "haiku" },
-      "stt": { "provider": "local", "model": "parakeet-tdt-0.6b-v3-int8" },
-      "tts": { "provider": "local", "model": "kokoro-en-v0_19", "speakerId": 0 }
-    }
-  },
-  "providers": {
-    "local": {
-      "modelsDir": "~/.paseo/models/local-speech"
-    }
-  }
-}`}
-        
-
- -
-

OpenAI Speech Option

-

- You can switch dictation, voice STT, and voice TTS to OpenAI by setting provider fields to{" "} - openai and providing{" "} - OPENAI_API_KEY. -

-
-          {`{
-  "version": 1,
-  "features": {
-    "dictation": { "stt": { "provider": "openai" } },
-    "voiceMode": {
-      "stt": { "provider": "openai" },
-      "tts": { "provider": "openai" }
-    }
-  },
-  "providers": {
-    "openai": { "apiKey": "..." }
-  }
-}`}
-        
-
- -
-

Environment Variables

-
    -
  • - OPENAI_API_KEY — OpenAI speech credentials -
  • -
  • - PASEO_VOICE_LLM_PROVIDER — voice agent provider - override -
  • -
  • - PASEO_LOCAL_MODELS_DIR — local model storage - directory -
  • -
  • - PASEO_DICTATION_LOCAL_STT_MODEL — local dictation STT - model ID -
  • -
  • - PASEO_VOICE_LOCAL_STT_MODEL,{" "} - PASEO_VOICE_LOCAL_TTS_MODEL — local voice STT/TTS - model IDs -
  • -
  • - PASEO_VOICE_LOCAL_TTS_SPEAKER_ID,{" "} - PASEO_VOICE_LOCAL_TTS_SPEED — optional local voice - TTS tuning -
  • -
-
- -
-

Operational Notes

-

- Realtime voice can launch and control agents. Treat voice prompts with the same care as - direct agent instructions, especially when specifying working directories or destructive - operations. -

-
-
- ); -} diff --git a/packages/website/src/routes/docs/worktrees.tsx b/packages/website/src/routes/docs/worktrees.tsx deleted file mode 100644 index f45dc0449..000000000 --- a/packages/website/src/routes/docs/worktrees.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { pageMeta } from "~/meta"; - -export const Route = createFileRoute("/docs/worktrees")({ - head: () => ({ - meta: pageMeta( - "Git Worktrees - Paseo Docs", - "Run agents in isolated git worktrees with setup hooks, scripts, and long-running services.", - ), - }), - component: Worktrees, -}); - -function Code({ children }: { children: React.ReactNode }) { - return ( -
- {children} -
- ); -} - -function Worktrees() { - return ( -
-
-

Git Worktrees

-

- Each agent runs in its own git worktree — a separate directory on a separate branch — so - parallel agents never step on each other. You configure setup, scripts, and long-running - services through a paseo.json file at your repo root. -

-
- - {/* Layout & workflow */} -
-

Layout and workflow

-

- Worktrees live under $PASEO_HOME/worktrees/, grouped by - a hash of the source checkout path. Each worktree gets a random slug; the branch name is - chosen when you first launch an agent. -

- -
{`~/.paseo/worktrees/
-└── 1vnnm9k3/               # hash of source checkout path
-    ├── tidy-fox/           # worktree slug (branch set on first agent)
-    └── bold-owl/`}
-
-
    -
  1. Create a worktree — Paseo runs your setup hooks
  2. -
  3. Launch an agent — a branch is created or assigned
  4. -
  5. Review the diff against the base branch
  6. -
  7. Merge or archive — archive runs teardown and removes the directory
  8. -
-
- - {/* paseo.json */} -
-

paseo.json

-

- Drop a paseo.json in your repo root. Paseo reads it - from the committed version of the base branch you picked, so uncommitted changes in other - branches don't apply. -

- -
{`{
-  "worktree": {
-    "setup":    "npm ci",
-    "teardown": "rm -rf .cache"
-  },
-  "scripts": {
-    "test": { "command": "npm test" },
-    "web":  { "command": "npm run dev", "type": "service", "port": 3000 }
-  }
-}`}
-
-
- - {/* Setup & teardown */} -
-

Setup and teardown

-

- setup runs once after the worktree is created. A fresh - worktree has no installed dependencies and no ignored files (like{" "} - .env), so use setup to install and copy what you need. - teardown runs during archive, before the directory is - removed. -

- -
{`{
-  "worktree": {
-    "setup": "npm ci\\ncp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env\\" .env\\nnpm run db:migrate",
-    "teardown": "npm run db:drop || true"
-  }
-}`}
-
-

- Both fields accept a multiline shell script or an array of commands; commands run - sequentially either way. -

-

- Commands run with the worktree as cwd. Use{" "} - $PASEO_SOURCE_CHECKOUT_PATH to reach files in the - original checkout (untracked config, local caches, etc). -

-
- - {/* Scripts & services */} -
-

Scripts and services

-

- scripts are named commands you can run inside a - worktree on demand. Mark one as a service and Paseo supervises it as a - long-running process, assigns it a port, and routes HTTP traffic to it through the - daemon's reverse proxy. -

- -

Plain scripts

- -
{`{
-  "scripts": {
-    "test":     { "command": "npm test" },
-    "lint":     { "command": "npm run lint" },
-    "generate": { "command": "npm run codegen" }
-  }
-}`}
-
- -

Services

- -
{`{
-  "scripts": {
-    "web": {
-      "type": "service",
-      "command": "npm run dev -- --port $PASEO_PORT",
-      "port": 3000
-    },
-    "api": {
-      "type": "service",
-      "command": "npm run api -- --port $PASEO_PORT"
-    }
-  }
-}`}
-
-

- Omit port to let Paseo auto-assign one. Bind your - process to $PASEO_PORT rather than hard-coding — each - worktree gets a distinct port so multiple copies of the same service coexist. -

- -

Reverse proxy

-

- Every service is reachable through the daemon at a deterministic hostname: -

- -
{`http://